1HPlease press Y for yes or N for no. 3663Y105N118$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
W E L C O M E T O
A D A - T U T R
T H E I N T E R A C T I V E A D A T U T O R
by John Herro
Software Innovations Technology Serial # 12330
1083 Mandarin Drive NE Version 1.00
Palm Bay, FL 32905-4706 25-MAR-1988
(407) 951-0233 Copyright 1988 John J. Herro
Shareware: Try ADA-TUTR free. To use it after the free trial, individ-
uals must register and organizations must buy a license. Both are very
inexpensive, and give many benefits. See page 1 of PRINT.ME for infor-
mation. Whether or not you use ADA-TUTR and register or buy a license,
please copy the complete program and give unmodified copies to others!
Have you used ADA-TUTR before?
1H Please press Y for yes or N for no. You need not hit ENTER. 1468#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
Welcome back!
Please type the number where you left off.
(If you don't know the number, type 106 and I'll take you to the main menu.)
Please press ENTER after the number.
2354A104B107C108D109E110F111G112H113I114J115K116L117$$$$$$$$$$$$ MAIN MENU
A Restart the Program. G Records, Arrays, and Assignment 3
B Introduction H Recursion and Assignment 4
C The Format of an Ada Program I Subprograms and Packages
D Generic Instantiation and J Additional Types, Exceptions,
Assignment 1 TEXT_IO, and Assignment 5
E Simple Declarations and Simple K Generics, Tasking, and Assignment 6
Attributes
L Advanced Topics
F Operators, Control Constructs, and
Assignment 2 X Exit the Program.
1HPlease press a letter. 1958A118B119C121D122E123F129M106$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ INTRODUCTION
MENU
A Welcome
B Printed Course Notes
C Do I Need an Ada Compiler for this Course?
D What is Ada?
E A Very Brief History of Ada
F What is a "Validated" Ada Compiler?
M Go Back to the Main Menu.
X Exit the Program.
1HPlease press a letter. 1973A135B137C147D149E150F158G159M106$$$$$$$$$$$$$$$$$$$$$$$$$$$$ THE FORMAT OF AN ADA PROGRAM
MENU
A Our First Ada Program F Numbers
B Local Declarations G Making the Dot Notation
Automatic
C Capitalization and Spacing
D Comments M Go Back to the Main Menu.
E Identifiers X Exit the Program.
1HPlease press a letter. 1912A167B173C175D178M106$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ GENERIC INSTANTIATION AND ASSIGNMENT 1
MENU
A Displaying Integers
B Generic Instantiation
C Outside Assignment 1 -
Preparing to Run Ada on Your Computer
D The Ada Library
M Go Back to the Main Menu.
X Exit the Program.
1HPlease press a letter. 1850A184B190C199D201M106$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ SIMPLE DECLARATIONS AND SIMPLE ATTRIBUTES
MENU
A Variables and Constants
B Enumeration Types
C Subtypes
D Simple Attributes
M Go Back to the Main Menu.
X Exit the Program.
1HPlease press a letter. 2243A216B218C223D231E240F242G248H250I255J261K264M106$$$$$$$$$$$$ OPERATORS, CONTROL CONSTRUCTS, AND ASSIGNMENT 2
MENU
A Operators H Labels and GOTOs
B Range Tests I The CASE Construct
C The Short Circuit Forms J Brief Overview of Functions
D The IF Block K Outside Assignment 2 -
Exercise in Enumeration Types
E WHILE Loops
F FOR Loops M Go Back to the Main Menu
G The EXIT Statement X Exit the Program.
1HPlease press a letter. 2066A271B279C288D298E310F312M106$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ RECORDS, ARRAYS, AND ASSIGNMENT 3
MENU
A Records
B Arrays
C Multidimensional Arrays
D Strings
E Array Operators
F Outside Assignment 3 -
Exercise in Records
M Go Back to the Main Menu.
X Exit the Program.
1HPlease press a letter. 1844A318B325C330M106$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ RECURSION AND ASSIGNMENT 4
MENU
A Recursion
B The Tower of Hanoi Problem
C Outside Assignment 4 -
Exercise in Recursion
M Go Back to the Main Menu.
X Exit the Program.
1HPlease press a letter. 2036A334B344C351D359E367F382M106$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ SUBPROGRAMS AND PACKAGES
MENU
A Procedures and Functions
B Default Parameters
C Packages
D Functions with Infix Notation
E Information Hiding: Private Types
F Type TEXT and Limited Private Types
M Go Back to the Main Menu.
X Exit the Program.
1HPlease press a letter. 2070A396B413C415D421E438F448M106$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ADDITIONAL TYPES, EXCEPTIONS, TEXT_IO, AND ASSIGNMENT 5
MENU
A Access Types
B User Defined Types and Portability
C Derived Types
D Exceptions
E More About TEXT_IO
F Outside Assignment 5 -
Writing a Simple Line Editor
M Go Back to the Main Menu.
X Exit the Program.
1HPlease press a letter. 1832A455B463C483M106$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ GENERICS, TASKING, AND ASSIGNMENT 6
MENU
A Generics
B Tasking
C Outside Assignment 6 -
Exercise in Tasking
M Go Back to the Main Menu.
X Exit the Program.
1HPlease press a letter. 2371A487B489C491D496E503F512G515H521I522J529K532L538M106$$$$$$$$ ADVANCED TOPICS
MENU
A Renaming H Subprogram Parameters with Generics
B Packages STANDARD and ASCII I Representation Clauses and SYSTEM
C An Alternative to Infix Notation J Unchecked Conversion and Unchecked
Deallocation
D Record Discriminants and Record
Variants K Pragmas
E Fixed Point and Universal Types L Loose Ends and Pitfalls
F More Attributes M Go Back to the Main Menu.
G SEQUENTIAL_IO and DIRECT_IO X Exit the Program.
1HPlease press a letter. 3456 119B104$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
WELCOME TO ADA-TUTR, THE INTERACTIVE ADA TUTOR!
ADA-TUTR will make you an excellent Ada programmer in minimum time. You should
be familiar with at least one other high-level language (Basic, Pascal, etc.)
You can study as long as you like. Whenever you're ready to exit the program,
just type X. Remember, typing X stops ADA-TUTR at any time. I always ask you
if you really want to exit the program. This gives you a chance to go back to
where you were, in case you typed X by mistake. When you're ready to learn
again, you'll be able to continue exactly where you left off.
ADA-TUTR works with monochrome as well as color monitors. Please adjust the
brightness and contrast of your monitor now. The highlighted words should be
noticeably brighter than the others, but everything should be easy to read.
Your questions, comments, and suggestions are encouraged. Please send them to
Software Innovations Technology, Attention: John J. Herro
1083 Mandarin Drive NE, Palm Bay, FL 32905-4706
1HPlease type a space to go on. (You need not hit ENTER.) 3031 120B118$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ PRINTED COURSE NOTES
To get the most from ADA-TUTR, you should have a printed copy of the PRINT.ME
file. If you haven't started printing the course notes yet, read this screen
and then type X to exit the program temporarily. Remember, you can leave
ADA-TUTR at any time, and later pick up exactly where you left off.
Then, when the system prompt (usually C>) appears, type:
PRINT PRINT.ME
You can then restart ADA-TUTR while your course notes are being printed.
Please read through page 3 now. If you got this far, you don't need page 4.
Please read page 5 if you want to install ADA-TUTR on a non-PC computer, such
as a mainframe.
If you're using a PC and you ever want to print the screen that's currently
being displayed, simply hold a Shift key while pressing the PrtSc key.
Whenever you exit ADA-TUTR, I give you a number telling where you left off.
(If you leave from here, the number will be 119.) When you run ADA-TUTR again,
I'll ask you to type that number. If you type 119, you'll be right back here.
1HPlease type X to exit, a space to go on, or B to go back. 2933 121B119$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$In addition to the printed course notes, we recommend (but don't require) that
you have access to the Military Standard Ada Programming Language manual,
ANSI/MIL-STD-1815A, 1983. This volume is often called the Language Reference
Manual, or LRM. You'll probably find it in a public library, or perhaps in
your company library. If you can borrow an LRM, it's legal to copy the entire
book. You can also buy an LRM very inexpensively.
Although the LRM is a good reference, it's difficult reading and not a good
learning tool. With ADA-TUTR you probably won't need a textbook, but if you
feel you do, try one of the following:
1. S. J. Young, AN INTRODUCTION TO ADA, Wiley and Sons, 1984
2. J. G. P. Barnes, PROGRAMMING IN ADA, Addison-Wesley, 1983
3. G. Booch, SOFTWARE ENGINEERING WITH ADA, Benjamin-Cummings, 1983
We have no financial interest in any of these authors or publishers; we simply
selected three books out of many. We don't recommend using books dated before
1983, because the Ada language changed then, and you'll be confused.
1HPlease type a space to go on, or B to go back. 2648 122B120$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ DO I NEED AN ADA COMPILER FOR THIS COURSE?
An Ada compiler is helpful, but not required. As we go along, there will be
six Outside Assignments; most of them ask you to write and compile a short Ada
program. If you don't have access to an Ada compiler, just skip the Outside
Assignments.
A brief list of Ada compilers available for the PC and compatibles is in your
printed course notes, on pages 2-3. We tried to include all the popular Ada
compilers, as we have no financial interest in any company mentioned in that
list. However, we might have missed some. If you know of an Ada compiler that
you think should be on that list, please contact us.
A screen editor / word processor is very helpful, and we can send you a copy of
one free or nearly free. Please see page 3 of your printed course notes for
details.
That's enough discussion of what's recommended for ADA-TUTR. Let's begin!
1HPlease type a space to go on, or B to go back. 3020 123B121$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ WHAT IS ADA?
Ada is a modern language designed for programming large scale and real time
systems. Ada is also an excellent general purpose language. It has many new
features which help prevent errors and detect errors earlier.
However, Ada is much more than a new language. It's also a new programming
philosophy. Beware of learning just "Ada syntax"; you'll wind up writing
Basic-like (or Pascal-like, etc.) programs that happen to be coded in Ada. If
you do that, you'll lose the many benefits of Ada. Unfortunately, you won't
see why Ada is a new programming philosophy until later in this course. (By
the time we get to packages and information hiding, you'll be an enthusiastic
convert.) In the meantime, please take our word for it: Ada is a whole new
programming philosophy, not just another language.
Because of its many features, Ada is relatively complicated. There's about
seven times as much to learn as Pascal. However, the effort pays great
dividends. Once you learn the language, Ada programs are easier to write,
easier to read, and much easier to modify a month or a year later.
1HPlease type a space to go on, or B to go back. 2726 124B122$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ A VERY BRIEF HISTORY OF ADA
Ada was named for Augusta Ada Byron, countess of Lovelace and daughter of the
poet Lord Byron. She worked with Charles Babbage on his Analytical Engine, and
has been called the world's first programmer. The Language Reference Manual
(LRM) was given the number ANSI/MIL-STD-1815A because Augusta Ada Byron was
born in 1815.
Ada was invented because the U.S. Department of Defense (DoD) realized that
none of the existing languages was very suitable for real-time control of
large, embedded systems. An embedded system is a computer system inside a
product other than a computer, such as an intelligent oven or a guided missile.
In 1977 the DoD issued a Request for Proposal to develop a new language; the
leading four contenders were designated Blue, Red, Yellow, and Green.
Eventually Green was selected to be Ada; it was designed by the company
Honeywell Bull under the direction of Jean Ichbiah of France.
1HPlease type a space to go on, or B to go back. 18172125112631274128B123$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$In the author's opinion, which one of the following statements is true?
1. Ada is simpler than Pascal.
2. Although Ada was designed for embedded systems, it makes a good general
purpose language.
3. Basic and Pascal programs can easily be translated into good Ada programs.
4. In languages that are simpler than Ada, errors are usually detected earlier
than in Ada.
1HPlease press 1, 2, 3, or 4, or B to go back. 1431 129B124Q124$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$You're right! Ada makes use of the latest advances in software engineering, so
it makes an excellent general purpose language.
1HPlease type a space to go on, or B or Q to go back to the question. 1850 129B124Q124$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$No, Ada is more complicated than Pascal. So far as the philosophy is
concerned, Ada is an extension (or "superset") of Pascal. However, in syntax
Ada isn't a superset of Pascal; the Ada syntax is different.
Although Ada is more complicated than Pascal (and many other languages), it has
many advantages. These advantages pay dividends in reduced effort to develop
and maintain programs.
1HPlease type a space to go on, or B or Q to go back to the question. 1515 129B124Q124$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$No, it might be easy to translate Basic and Pascal programs into Ada, but the
resulting programs wouldn't be very good, because they'd fail to take advantage
of the features of Ada.
1HPlease type a space to go on, or B or Q to go back to the question. 1756 129B124Q124$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$No, Ada has a reputation for detecting errors earlier than the simpler
languages. Ada detects errors at compile time that other languages detect at
run time or not at all. Examples are calling a subprogram with the wrong
number of arguments, and unintentionally mixing different types in an
expression. We'll learn more about these things later.
1HPlease type a space to go on, or B or Q to go back to the question. 2917T130F131B124$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ WHAT IS A "VALIDATED" ADA COMPILER?
Although the name "Ada" is no longer a trademark of the Department of Defense,
the Ada Joint Program Office (AJPO) still validates Ada compilers. Only
validated compilers may display an emblem that says, "Validated Ada ... This
product conforms to ANSI/MIL-STD-1815A as determined by the AJPO under its
current testing procedures."
If a compiler conforms exactly to the standard, and isn't a subset or superset
of Ada, it can be validated after passing an extensive suite of tests (called
the Ada Compiler Validation Capability, or ACVC). The Validation Certificate
is good for only one year, after which the compiler must be retested. This is
done because the ACVC keeps growing.
An unnumbered page in the beginning of the LRM says that "Only compilers which
have been validated ... shall be used in DoD systems." Also, a 1987 DoD
Directive (3405.2) says that only validated Ada compilers may be used on
mission critical systems.
True or False? Subsets and supersets of standard Ada may be called "Ada."
1HPlease press T for true or F for false, or B to go back. 1919 132B129Q129$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$You're right! Compilers not conforming exactly to the Ada standard may be
called "Ada." This was true even when "Ada" was a DoD trademark. A preface to
the LRM says that compilers not conforming to the standard may be called "Ada"
if there's a clear statement that they don't conform.
Of course, only validated Ada compilers are to be used on DoD systems, and
validated compilers can't be subsets or supersets of standard Ada.
1HPlease type a space to go on, or B or Q to go back to the question. 1922 132B129Q129$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$True. It's a common misconception that subsets and supersets may not be called
"Ada." However, this was allowed even when "Ada" was a DoD trademark. A
preface to the LRM says that compilers not conforming to the standard may be
called "Ada" if there's a clear statement that they don't conform.
It's true that only validated Ada compilers are to be used in DoD systems.
Validated compilers can't be subsets or supersets of standard Ada.
1HPlease type a space to go on, or B or Q to go back to the question. 1361F133T134B129$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$True or False? The Validation tests are performed to provide reasonable
assurance that a compiler is bug-free.
1HPlease press T for true or F for false, or B to go back. 1572 135B132Q132$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$You're right! The Validation tests are performed to make sure that a compiler
conforms to the Ada standard, and isn't a subset or superset. A compiler isn't
believed to be bug-free just because it earned a Validation Certificate.
1HPlease type a space to go on, or B or Q to go back to the question. 1616 135B132Q132$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$False. That's a common misconception, but validation only determines that a
compiler conforms exactly to the Ada standard, and isn't a subset or superset.
A compiler isn't believed to be bug-free just because it earned a Validation
Certificate.
1HPlease type a space to go on, or B or Q to go back to the question. 3021 136B132$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ OUR FIRST ADA PROGRAM
Now let's look at a simple Ada program. This program merely prints "Hello!" on
the terminal.
with TEXT_IO;
procedure HELLO is
begin
TEXT_IO.PUT_LINE("Hello!");
end HELLO;
TEXT_IO is a "package" that comes with the Ada language. We'll learn more
about packages later. TEXT_IO contains, among other things, a procedure
PUT_LINE that takes one argument of type STRING and prints it on the terminal.
In this case, the STRING argument is "Hello!". Ada doesn't have any special
I/O statements. I/O is done by calling procedures that Ada provides for us.
Even though the package TEXT_IO comes with Ada, our procedure HELLO can't "see"
it unless it says with TEXT_IO;. With that statement, our procedure can call
any procedure or function inside TEXT_IO. We call a procedure by giving the
package name (TEXT_IO), followed by a dot and the name of the procedure within
the package. Like Pascal and unlike Fortran, the word CALL isn't used in Ada.
1HPlease type a space to go on, or B to go back. 3037 137B135$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO;
procedure HELLO is
begin
TEXT_IO.PUT_LINE("Hello!");
end HELLO;
The statement with TEXT_IO; is called a context clause because it specifies the
"context" in which procedure HELLO is compiled.
In Ada a procedure can be called either from another procedure or as the main
program. In this example, HELLO is the main program; it calls PUT_LINE.
Every Ada statement, including the last, ends with a semicolon. (Technically,
the lines above without semicolons aren't statements.) In Pascal, semicolons
come between statements, but in Ada, a semicolon ends a statement.
In Ada, the end statement may optionally give the name of the procedure or
function. If a name is given, the compiler will check that it agrees with the
name at the beginning. If it doesn't agree, the compiler will issue a message.
The statements between begin and end (this program has only one) are called
executable statements; when they're obeyed they're said to be executed.
1HPlease type a space to go on, or B to go back. 3059 138B136$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ LOCAL DECLARATIONS
with TEXT_IO;
procedure HELLO is
I : INTEGER;
X, Y : FLOAT;
begin
TEXT_IO.PUT_LINE("Hello!");
end HELLO;
Local declarations may appear between procedure and begin. This simple program
doesn't need any, but we've added two declarative statements by way of example.
Executable statements are said to be executed when they're obeyed, but when a
declarative statement is obeyed, it's said to be elaborated. Declarative
statements are elaborated at run time, not compile time.
In this example, when the procedure is called (either from another procedure or
as the main program), the declarations are elaborated and variables I, X, and Y
are brought into existence. Then the executable statement(s) are obeyed. When
the end statement is reached and the procedure returns, I, X, and Y go out of
existence. These three variables are local to procedure HELLO and can't be
accessed from outside this procedure.
1HPlease type a space to go on, or B to go back. 2946 139B137$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO;
procedure HELLO is
I : INTEGER;
X, Y : FLOAT;
begin
TEXT_IO.PUT_LINE("Hello!");
end HELLO;
If HELLO is called again, I, X, and Y will again be brought into existence, but
their previous values won't necessarily be remembered between calls. (Of
course, in this simple example, I, X, and Y aren't given values anyway.)
Like Pascal and unlike Basic and Fortran, every Ada variable must be declared.
But unlike Pascal, Ada programs don't need to say "var". Ada knows that the
objects declared above are variables. Constant declarations contain the
reserved word constant and may or may not specify a type, as shown below.
Declarations are elaborated in order, top to bottom. These two declarations
must appear in the order shown, because the second refers to the first:
PI : constant := 3.141592654;
TWO_PI : constant FLOAT := 2.0 * PI;
1HPlease type a space to go on, or B to go back. 1750314011412142B138$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1 with TEXT_IO;
procedure HELLO is
2 I : INTEGER;
X, Y : FLOAT;
begin
3 TEXT_IO.PUT_LINE("Hello!");
end HELLO;
OK, in the program above, where are the executable statement(s)?
1HPlease press 1, 2, or 3, or B to go back. 1839 143B139Q139$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1 with TEXT_IO;
procedure HELLO is
2 I : INTEGER;
X, Y : FLOAT;
begin
3 TEXT_IO.PUT_LINE("Hello!");
end HELLO;
You're right! Executable statements are placed between begin and end.
1HPlease type a space to go on, or B or Q to go back to the question. 1865 143B139Q139$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1 with TEXT_IO;
procedure HELLO is
2 I : INTEGER;
X, Y : FLOAT;
begin
3 TEXT_IO.PUT_LINE("Hello!");
end HELLO;
No, with TEXT_IO; is a context clause. Executable statements are placed
between begin and end.
1HPlease type a space to go on, or B or Q to go back to the question. 1943 143B139Q139$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1 with TEXT_IO;
procedure HELLO is
2 I : INTEGER;
X, Y : FLOAT;
begin
3 TEXT_IO.PUT_LINE("Hello!");
end HELLO;
No, declarative statements are placed between procedure and begin. Executable
statements are placed between begin and end.
1HPlease type a space to go on, or B or Q to go back to the question. 1726214411453146B139$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1 with TEXT_IO;
procedure HELLO is
2 I : INTEGER;
X, Y : FLOAT;
begin
3 TEXT_IO.PUT_LINE("Hello!");
end HELLO;
Now, where are the local declaration(s)?
1HPlease press 1, 2, or 3, or B to go back. 1842 147B143Q143$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1 with TEXT_IO;
procedure HELLO is
2 I : INTEGER;
X, Y : FLOAT;
begin
3 TEXT_IO.PUT_LINE("Hello!");
end HELLO;
You're right! Local declarations are placed between procedure and begin.
1HPlease type a space to go on, or B or Q to go back to the question. 1868 147B143Q143$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1 with TEXT_IO;
procedure HELLO is
2 I : INTEGER;
X, Y : FLOAT;
begin
3 TEXT_IO.PUT_LINE("Hello!");
end HELLO;
No, with TEXT_IO; is a context clause. Local declarations are placed between
procedure and begin.
1HPlease type a space to go on, or B or Q to go back to the question. 1939 147B143Q143$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1 with TEXT_IO;
procedure HELLO is
2 I : INTEGER;
X, Y : FLOAT;
begin
3 TEXT_IO.PUT_LINE("Hello!");
end HELLO;
No, executable statements are placed between begin and end. Local declarations
are placed between procedure and begin.
1HPlease type a space to go on, or B or Q to go back to the question. 2627 148B143$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ CAPITALIZATION AND SPACING
with TEXT_IO;
procedure HELLO is
I : INTEGER;
X, Y : FLOAT;
begin
TEXT_IO.PUT_LINE("Hello!");
end HELLO;
Except in quoted STRINGs like "Hello!", capitalization isn't important in Ada.
The LRM shows all reserved words in lower case and all identifiers in upper
case, so that's the style we're using in our examples.
In Ada, unlike Fortran, statements may begin anywhere on the line; columns
aren't significant. Most people indent the local declarations and the
executable statements three spaces, as shown above. Spaces and tabs may not
appear within reserved words and identifiers, but they may freely be used
between elements of the program.
1HPlease type a space to go on, or B to go back. 2456 149B147$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO;
procedure HELLO is
I : INTEGER;
X, Y : FLOAT;
begin
TEXT_IO.PUT_LINE("Hello!");
end HELLO;
Since a semicolon marks the end of a statement, we can put several statements
on one line, like this:
I : INTEGER; X, Y : FLOAT;
We can also continue a statement over several lines, breaking anywhere a space
would be permitted, like this:
TEXT_IO.
PUT_LINE(
"Hello!");
Most people follow the capitalization and spacing style of the LRM, so we'll do
that in this course.
1HPlease type a space to go on, or B to go back. 2847 150B148$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ COMMENTS
with TEXT_IO;
procedure HELLO is
-- This program prints "Hello!" on the screen.
I : INTEGER; -- These declarations aren't needed; they
X, Y : FLOAT; -- were added only to provide examples.
begin
TEXT_IO.PUT_LINE("Hello!");
end HELLO;
Comments in Ada begin with a double hyphen (double minus sign) and continue
through the end of the line. No space is permitted inside the double hyphen.
As shown, a comment can be an entire line, or it can follow code on a line.
Ada programs generally have less need for comments than programs written in
other languages. The reason is that, as we'll see, the identifiers can be as
long as we like and can have very meaningful names. Also, when we learn about
named notation, we'll see that calls to subprograms can be made much more
readable in Ada than in other languages. These features and others tend to
make Ada programs self documenting.
1HPlease type a space to go on, or B to go back. 3055 151B149$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ IDENTIFIERS
with TEXT_IO;
procedure HELLO is
I : INTEGER;
X, Y : FLOAT;
begin
TEXT_IO.PUT_LINE("Hello!");
end HELLO;
In this program, everything in capital letters is an identifier. Ada
identifiers may contain letters, digits, and underlines, but must start with a
letter. They may not contain spaces or other characters. Identifiers may be
as long as you like, up to the length of a line. (The maximum line length
depends on the particular implementation of Ada.) All characters are
significant, so THIS_IS_A_LONG_IDENTIFIER_1 and THIS_IS_A_LONG_IDENTIFIER_2 are
distinct. Since underlines are significant, A_B is different from AB. Every
underline must be followed by a letter or a digit. Thus, you can't have two
underlines together, and an identifier can't end in an underline.
Reserved words may not be used as identifiers. The 63 Ada reserved words are
listed on page 6 of your printed course notes, and in section 2.9 of the LRM.
1HPlease type a space to go on, or B to go back. 1658315211532154415551566157B150$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. 1553B
2. BEGIN
3. This_Is_a_Very_Long_Name_for_an_Identifier
4. SYS$QIO
5. NUMBER_OF_TARGETS_
6. MAX SPEED
Only one of the above is a legal Ada identifier. Which is it?
1HPlease press 1, 2, 3, 4, 5, or 6, or B to go back. 2022 158B151Q151$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. 1553B
2. BEGIN
3. This_Is_a_Very_Long_Name_for_an_Identifier
4. SYS$QIO
5. NUMBER_OF_TARGETS_
6. MAX SPEED
You're right! An Ada identifier can be as long as we like (up to the length of
a line), and upper/lower case isn't important in Ada.
Number 1 begins with a digit, 2 is a reserved word, 4 has a dollar sign, 5 has
a trailing underline, and 6 has a space.
1HPlease type a space to go on, or B or Q to go back to the question. 1715 158B151Q151$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. 1553B
2. BEGIN
3. This_Is_a_Very_Long_Name_for_an_Identifier
4. SYS$QIO
5. NUMBER_OF_TARGETS_
6. MAX SPEED
No, number 1 is illegal because Ada identifiers must begin with a letter.
1HPlease type a space to go on, or B or Q to go back to the question. 1670 158B151Q151$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. 1553B
2. BEGIN
3. This_Is_a_Very_Long_Name_for_an_Identifier
4. SYS$QIO
5. NUMBER_OF_TARGETS_
6. MAX SPEED
No, number 2 is illegal because begin is a reserved word.
1HPlease type a space to go on, or B or Q to go back to the question. 1761 158B151Q151$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. 1553B
2. BEGIN
3. This_Is_a_Very_Long_Name_for_an_Identifier
4. SYS$QIO
5. NUMBER_OF_TARGETS_
6. MAX SPEED
No, number 4 is illegal because of the dollar sign. Ada identifiers may
contain only letters, digits, and underlines.
1HPlease type a space to go on, or B or Q to go back to the question. 1914 158B151Q151$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. 1553B
2. BEGIN
3. This_Is_a_Very_Long_Name_for_an_Identifier
4. SYS$QIO
5. NUMBER_OF_TARGETS_
6. MAX SPEED
No, number 5 is illegal because of the trailing underline. Every underline
must be followed by a letter or a digit. You can't have a trailing underline,
and you can't have two underlines together.
1HPlease type a space to go on, or B or Q to go back to the question. 1755 158B151Q151$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. 1553B
2. BEGIN
3. This_Is_a_Very_Long_Name_for_an_Identifier
4. SYS$QIO
5. NUMBER_OF_TARGETS_
6. MAX SPEED
No, number 6 is illegal because of the space. Ada identifiers may contain only
letters, digits, and underlines.
1HPlease type a space to go on, or B or Q to go back to the question. 2954 159B151$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ NUMBERS
In numbers, unlike identifiers, underlines are not significant. Thus 12345 and
12_345 are equivalent. Spaces aren't permitted within numbers. When an
underline appears in a number, it must be surrounded by digits. Thus, all of
these are illegal: 123_ 123_.0 _123 12__3
Floating point numbers must have at least one digit before and one digit after
the decimal point. Unlike Basic and Fortran, Ada forces us to write 1.0
instead of 1., and 0.5 instead of .5. Scientific notation may be used: 1.3E-3
is the same as 0.0013. Non-negative exponents may be used even in integers:
12E3 means 12_000.
Numbers may be specified in any base from 2 to 16, as shown below. These three
numbers are all equal:
16#FC03# 2#1111_1100_0000_0011# 8#176003#
A number with a base may have an exponent. The number after the E is still
written in decimal, and gives the power by which the base is raised. Thus
2#110#E5 is 6 times 2**5, or 192.
1HPlease type a space to go on, or B to go back. 2957 160B158$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ MAKING THE DOT NOTATION AUTOMATIC
These two programs are equivalent:
with TEXT_IO; with TEXT_IO; use TEXT_IO;
procedure HELLO is procedure HELLO is
begin begin
TEXT_IO.PUT_LINE("Hello!"); PUT_LINE("Hello!");
end HELLO; end HELLO;
We've said that the statement with TEXT_IO; makes the package visible, so that
our program can call the procedures and functions in it. To call a procedure,
we write the name of the package, a dot, and the name of the procedure.
The statement use TEXT_IO; tells the compiler to supply the name of the package
and the dot for us. That's why the two programs above are equivalent.
Remember, in Ada with provides visibility; use asks the compiler to supply the
package name and the dot.
The Ada meanings of the words with and use are more or less reversed from their
meanings in Pascal. Also, in Ada one can't use records, only packages.
1HPlease type a space to go on, or B to go back. 3040 161B159$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO; with TEXT_IO; use TEXT_IO;
procedure HELLO is procedure HELLO is
begin begin
TEXT_IO.PUT_LINE("Hello!"); PUT_LINE("Hello!");
end HELLO; end HELLO;
A program can with and use several packages. For example, there's a package
that comes with Ada called CALENDAR. Suppose we've also compiled two packages
of our own, called MY_PKG_1 and MY_PKG_2. Then our program might say
with TEXT_IO, CALENDAR, MY_PKG_1, MY_PKG_2;
use TEXT_IO, CALENDAR, MY_PKG_1, MY_PKG_2;
In this case, when the compiler sees the call to PUT_LINE, it will search all
four packages that we've used for a procedure PUT_LINE that takes one STRING
argument. If it finds no procedure PUT_LINE, the compiler will print an error
message, like "PUT_LINE is not declared" or "Undeclared identifier PUT_LINE."
You may ask, what if there are several procedures PUT_LINE among the four
packages? Will the compiler stop when it finds the first PUT_LINE? No.
1HPlease type a space to go on, or B to go back. 2751 162B160$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO, CALENDAR, MY_PKG_1, MY_PKG_2;
use TEXT_IO, CALENDAR, MY_PKG_1, MY_PKG_2;
If the compiler finds several PUT_LINEs, the name PUT_LINE is said to be
overloaded. In that case the compiler will use the number and types of
arguments to try to select the right PUT_LINE. For example, if TEXT_IO
contains a PUT_LINE that takes one STRING argument, and MY_PKG_1 contains a
PUT_LINE that takes one INTEGER argument, the compiler will write a call to
TEXT_IO.PUT_LINE and not MY_PKG_1.PUT_LINE, because the calling statement
supplies one STRING argument.
If there's more than one PUT_LINE that takes exactly one STRING argument, then
the call is ambiguous and the compiler can't resolve the overloading. The
error message will be something like "Ambiguity detected during overload
resolution" or "Ambiguous expression." In that case, we'd have to specify
TEXT_IO.PUT_LINE even though we said use TEXT_IO;.
1HPlease type a space to go on, or B to go back. 2524 163B161$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$In summary, the compiler will search all the packages that we've used for a
procedure with the correct number and types of arguments. If it finds exactly
one, everything's fine. If it finds no procedure with the correct name, the
error message will be something like "Undeclared identifier." If it finds one
or more procedures with the correct name, but none of them has the right number
and types of arguments, it will print an error message like "Inconsistency
detected during overload resolution" or "Unresolvable expression." Finally, if
it finds more than one procedure with the correct number and types of
arguments, the message is "Ambiguity detected during overload resolution" or
"Ambiguous expression."
Overloading may seem like an unnecessary complication at this point, but you'll
see later how very useful it can be.
1HPlease type a space to go on, or B to go back. 19134164116521663166B162$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Which one of the following would most likely be the cause of the message
"Inconsistency detected during overload resolution"?
1. You tried to use a package that you didn't with.
2. You misspelled the name of a package in a call using dot notation.
3. You misspelled the name of a procedure or function in a call.
4. You called a procedure or function with the wrong number or types of
arguments.
1HPlease press 1, 2, 3, or 4, or B to go back. 2020 167B163Q163$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. You tried to use a package that you didn't with.
2. You misspelled the name of a package in a call using dot notation.
3. You misspelled the name of a procedure or function in a call.
4. You called a procedure or function with the wrong number or types of
arguments.
You're right! The message "Inconsistency detected during overload resolution"
usually means that you called a procedure or a function with the wrong number
or types of arguments.
1HPlease type a space to go on, or B or Q to go back to the question. 1961 167B163Q163$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. You tried to use a package that you didn't with.
2. You misspelled the name of a package in a call using dot notation.
3. You misspelled the name of a procedure or function in a call.
4. You called a procedure or function with the wrong number or types of
arguments.
No, for number 1 the message would be something like "This package is not named
in a prior with clause," or "use clause was not expected here."
1HPlease type a space to go on, or B or Q to go back to the question. 1936 167B163Q163$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. You tried to use a package that you didn't with.
2. You misspelled the name of a package in a call using dot notation.
3. You misspelled the name of a procedure or function in a call.
4. You called a procedure or function with the wrong number or types of
arguments.
No, if you misspell the name of a package, procedure, or function, the most
probable error message would be "Undeclared identifier."
1HPlease type a space to go on, or B or Q to go back to the question. 2811 168B163$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ DISPLAYING INTEGERS
with TEXT_IO; use TEXT_IO; with TEXT_IO; use TEXT_IO;
procedure HELLO is THIS THIS procedure ADD is
begin <= IS IS => begin
PUT_LINE("Hello!"); RIGHT WRONG PUT_LINE(2 + 2);
end HELLO; end ADD;
Now let's write a program called ADD that adds 2 + 2 and prints the result.
You may think that we could take the HELLO program and substitute 2 + 2 for
"Hello!", but that won't work. (We never said Ada is easy!) Why won't it
work? Because TEXT_IO doesn't have a procedure PUT_LINE that takes an INTEGER
argument. One correct program is this:
with TEXT_IO; use TEXT_IO;
procedure ADD is
package MY_INT_IO is new INTEGER_IO(INTEGER); use MY_INT_IO;
begin
PUT(2 + 2);
NEW_LINE;
end ADD;
1HPlease type a space to go on, or B to go back. 3269 169B167$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO; use TEXT_IO;
procedure ADD is
package MY_INT_IO is new INTEGER_IO(INTEGER); use MY_INT_IO;
begin
PUT(2 + 2);
NEW_LINE;
end ADD;
__________ TEXT_IO __________ The package TEXT_IO contains procedures for type
| PUT_LINE for type STRING | STRING. This package is ready-to-use. However,
| PUT for type STRING | inside TEXT_IO is another package, INTEGER_IO,
| GET_LINE for type STRING | that's not ready-to-use. It's called a generic
| GET for type STRING | package because it has an empty box (<>) in
| NEW_LINE | place of the type. We can make a new, ready-to-
| ... | -use package from INTEGER_IO by giving the type:
| | package MY_INT_IO is new INTEGER_IO(INTEGER); we
| ______ INTEGER_IO ______ | could have used any name in place of MY_INT_IO.
| | PUT for type <> | | Note that we've declared our new package locally
| | GET for type <> | | inside the procedure ADD. MY_INT_IO now has the
| | ... | | same procedures and functions as INTEGER_IO, but
| |______________________| | with the empty box filled in with the type
|___________________________| INTEGER, like this:
1HPlease type a space to go on, or B to go back. 3222 170B168$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO; use TEXT_IO;
__________ TEXT_IO __________ procedure ADD is
| PUT_LINE for type STRING | package MY_INT_IO is new INTEGER_IO(
Since INTEGER_IO (and therefore MY_INT_IO) doesn't have a PUT_LINE, we call
MY_INT_IO.PUT and then TEXT_IO.NEW_LINE. Note that our program says
use MY_INT_IO; after declaring MY_INT_IO. When the compiler sees the call
PUT(2 + 2); it writes code to call MY_INT_IO.PUT rather than TEXT_IO.PUT,
because MY_INT_IO.PUT takes an argument of type INTEGER. The compiler then
finds NEW_LINE in TEXT_IO and writes a call to TEXT_IO.NEW_LINE.
1HPlease type a space to go on, or B to go back. 1270F171T172B169$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$True or False? A program can call INTEGER_IO.PUT.
1HPlease press T for true or F for false, or B to go back. 1570 173B170Q170$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$You're right! Since INTEGER_IO is generic, a program can't call its procedures
and functions. The program must specify the type and create an instance of
INTEGER_IO, such as MY_INT_IO. It can then call PUT in MY_INT_IO.
1HPlease type a space to go on, or B or Q to go back to the question. 1556 173B170Q170$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$False. Since INTEGER_IO is generic, a program can't call its procedures and
functions. The program must specify the type and create an instance of
INTEGER_IO, such as MY_INT_IO. It can then call PUT in MY_INT_IO.
1HPlease type a space to go on, or B or Q to go back to the question. 3265 174B170$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ GENERIC INSTANTIATION
package MY_INT_IO is new INTEGER_IO(INTEGER);
______ INTEGER_IO ______ ______ MY_INT_IO _______
| PUT for type <> | | PUT for type INTEGER |
| GET for type <> | | GET for type INTEGER |
| ... | | ... |
|______________________| |______________________|
This process of creating an instance of the generic package INTEGER_IO for the
type INTEGER is called generic instantiation. Later in this course we'll
learn to write our own generic packages, procedures, and functions. However,
we wanted to learn, early in the course, how to instantiate an already-written
generic package in order to display integers.
But why does Ada make TEXT_IO ready-to-use for STRINGs, while INTEGER_IO is
generic, and has to be instantiated for INTEGERs? Because programs normally
use only one type STRING, but can have several integer types. Right now, we
know of only one integer type, the standard INTEGER. Later we'll learn about
user-defined types and derived types, and we'll see how there can be several
integer types. INTEGER_IO can be instantiated for each of these types.
1HPlease type a space to go on, or B to go back. 2434 175B173$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Since INTEGER_IO is often instantiated for the standard INTEGER type, some
implementations of Ada provide an already-instantiated package for the type
INTEGER, equivalent to our package MY_INT_IO. However, an implementation of
Ada need not supply such a package to meet the Ada standard. Our program did
its own instantiation of INTEGER_IO so that it would run on any Ada that meets
the standard.
There's another generic package within TEXT_IO called FLOAT_IO. Input and
output of floating point numbers is done by instantiating FLOAT_IO for the type
FLOAT. As with integers, we'll later learn how there can be several floating
point types besides the standard FLOAT.
We're now ready for our first Outside Assignment! Let's compile and run our
two sample programs, HELLO and ADD.
1HPlease type a space to go on, or B to go back. 2936 176B174$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ OUTSIDE ASSIGNMENT 1 - PREPARING TO RUN ADA ON YOUR COMPUTER
Since these screens disappear, you'll probably want to refer to your printed
course notes when doing the Outside Assignments. The first assignment appears
on page 7. In this assignment, we'll compile, link, and run our first two
programs, HELLO and ADD.
First compile HELLO.ADA, then link, and then execute the program. The exact
steps will depend on the Ada compiler you're using. You'll probably have to
refer to the documentation that came with your compiler. Note that some
implementations of Ada use the term "binding" instead of "linking." When you
run the program, it should print Hello! on your screen.
Then compile ADD.ADA, link, and run the program.
Note that when we compile, we specify the name of the source file being
compiled. Many, but not all, implementations of Ada assume the extension to be
.ADA if it's not specified. When we link, we specify the name of the main
program, which in our examples agrees with the name of the source file. The
linking step produces a file which we can execute.
1HPlease type a space to go on, or B to go back. 2230 177B175$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Note that, before we can compile an Ada program, we must create our own Ada
library. Some implementations of Ada come with a library already created for
us; others require us to type a command to create a library. When we compile
HELLO.ADA and ADD.ADA, the procedures HELLO and ADD are added to our library.
We'll discuss the Ada library in more detail later.
If you haven't done so already, please type X to exit ADA-TUTR temporarily,
read page 7 of your printed course notes, and try the first Outside Assignment.
When you finish Outside Assignment 1, we'll go on to discuss the Ada library
and separate compilation of program units.
1HPlease type X to exit, a space to go on, or B to go back. 1918 178B176$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Congratulations on Completing Outside Assignment 1!
We know this first assignment wasn't very interesting, because the programs
HELLO.ADA and ADD.ADA were supplied for you. But we had to be sure we can
compile, link, and execute Ada programs, so we can do the remaining
assignments.
The rest of the Outside Assignments should be more challenging!
Let's go on to discuss the Ada library and separate compilation of program
units.
1HPlease type a space to go on, or B to go back. 2941 179B177$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ THE ADA LIBRARY
Why do we have to have our own library before running an Ada compiler, unlike
other compilers? Because compilers for other languages simply take source code
and produce object code. Ada compilers, however, take source code and a
library and produce object code and an updated library. The library remembers
what we've compiled.
________________
| |
Source Code | Fortran, | Object Code
----------------------->| Pascal, etc. |----------------------->
| Compiler |
|________________|
________________
Source Code | | Object Code
----------------------->| Ada |----------------------->
1HPlease type a space to go on, or B to go back. 3526 180B178$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$This gives us the advantage that Ada compilation is separate but dependent.
Some languages, such as Fortran, offer separate and independent compilation. A
Fortran main program and a subroutine can be compiled independently, perhaps a
month apart. When the subroutine is compiled, Fortran knows nothing about the
main program, and vice versa. An advantage of separate compilation is that we
can develop our program one piece at a time and compile each piece as it's
developed. However, the independent compilation is a disadvantage. The
compiler can't check that the number and types of arguments in the call agree
with the number and types of arguments in the subroutine. A Fortran main
program might say CALL SUB(I, J, K), while the subroutine might say
SUBROUTINE SUB(I, J). Both of these will compile correctly. When the program
is run, however, the results are unpredictable. The program might simply give
wrong answers with no warning.
Other languages, such as early Pascal and many versions of Basic, don't have
separate compilation at all. The compiler could check the number and types of
arguments in a call, because the entire program must be compiled at one time.
But the advantages of separate compilation are lost. We can't develop the
program one piece at a time, compiling each piece as it's developed. And if we
make the slightest change in one part of the program, the whole program has to
be recompiled.
1HPlease type a space to go on, or B to go back. 3442 181B179$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Ada gives us the advantages of both systems, because compilation is separate
but dependent. We can compile a subprogram today and the calling program next
month. (We could also put both into one file and compile them together.) Each
compilation unit updates the library when it's compiled. Thus, when a call to
a subprogram is encountered, the compiler will check the number and types of
arguments against the subprogram already in the library.
We could also write the calling program before the subprogram, because Ada lets
us compile the subprogram specification separately from its body. (We'll learn
more about that later.) The specification contains the name of the subprogram,
and the names, number, and types of any arguments. The specification also
tells which arguments are inputs, which are outputs, and which are both inputs
and outputs. That's all the information the compiler needs to compile calls to
the subprogram; the body can be supplied later. When the subprogram body is
compiled, the compiler will make sure it's consistent with the specification.
Ada comes with several packages, such as TEXT_IO. These are already compiled
and placed into the library. That's why our programs can say with TEXT_IO; and
call its procedures and functions.
Ada libraries are a complication compared to other languages, but they offer
great advantages in program development.
1HPlease type a space to go on, or B to go back. 1355T182F183B180$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$True or False? A procedure specification must be compiled before calls to the
procedure can be compiled.
1HPlease press T for true or F for false, or B to go back. 1419 184B181Q181$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$You're right! The specification contains all the information the compiler
needs to compile calls to the procedure.
1HPlease type a space to go on, or B or Q to go back to the question. 1453 184B181Q181$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$True. The compiler needs the information in the specification to compile calls
to the procedure. However, the procedure body can be compiled later.
1HPlease type a space to go on, or B or Q to go back to the question. 2853 185B181$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ VARIABLES AND CONSTANTS
procedure DECLARATIONS_DEMO is
I, J, K : INTEGER;
L, M : INTEGER := 30;
F, G : FLOAT;
FACTOR : constant := 1000;
begin
J := L + 10; -- Simple assignment statements.
F := 0.0;
G := F + L; -- ILLEGAL. Can't accidentally mix types.
G := F + FLOAT(L); -- Legal. Can deliberately convert types.
However, Ada lets us deliberately mix types. The statement G := F + FLOAT(L);
is legal because it converts L from INTEGER to FLOAT, adds it to F which is of
type FLOAT, and stores the result in G, also of type FLOAT. Likewise, the next
statement is legal because it converts F to INTEGER, adds the integer L, and
stores the result in the integer K. But note that when a FLOAT is converted to
INTEGER, it's rounded, not truncated. The last executable statement is illegal
because a constant can't appear on the left side of an assignment statement.
1HPlease type a space to go on, or B to go back. 191911882189B186$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO; use TEXT_IO;
procedure TRY_ME is
package MY_INT_IO is new INTEGER_IO(INTEGER); use MY_INT_IO;
N : INTEGER := 1;
begin
PUT(N);
NEW_LINE;
N := 2;
end TRY_ME;
What number will be displayed the second time procedure TRY_ME is called?
1. The program will display 1.
2. The program will display 2.
1HPlease press 1 or 2, or B to go back. 1845 190B187Q187$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO; use TEXT_IO;
procedure TRY_ME is
package MY_INT_IO is new INTEGER_IO(INTEGER); use MY_INT_IO;
N : INTEGER := 1;
begin
PUT(N);
NEW_LINE;
N := 2;
end TRY_ME;
You're right! N is brought into existence and initialized to 1 each time the
procedure is called, so 1 will be displayed.
1HPlease type a space to go on, or B or Q to go back to the question. 2027 190B187Q187$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO; use TEXT_IO;
procedure TRY_ME is
package MY_INT_IO is new INTEGER_IO(INTEGER); use MY_INT_IO;
N : INTEGER := 1;
begin
PUT(N);
NEW_LINE;
N := 2;
end TRY_ME;
No, N is declared locally within TRY_ME. So N is brought into existence and
initialized to 1 each time the procedure is called, and the procedure will
display 1 each time. Also remember that N goes out of existence when TRY_ME
returns.
1HPlease type a space to go on, or B or Q to go back to the question. 2743 191B187$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ENUMERATION TYPES
In Ada we can declare enumeration types, where we enumerate every possible
value for a type. For example, if we declare
type RAINBOW_COLOR is (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET);
RC : RAINBOW_COLOR;
then RC can have any of 7 values. In the executable region we might write
RC := RED; and we could also test: if RC = RED then ... end if;. The
enumerated values, enclosed in parentheses, must follow the rules for Ada
identifiers, or they can be single characters enclosed in ' marks (called "tic"
marks), thus:
type EVEN_DIGIT is ('0', '2', '4', '6', '8');
It's illegal to write type PROCESSOR is (8086, Z80, 1750A); because two of the
values aren't legal Ada identifiers. However, it's OK to mix characters and
identifiers in the same declaration, thus:
type MIXED is (BIG, SMALL, 'X', '9');
1HPlease type a space to go on, or B to go back. 2726 192B190$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type RAINBOW_COLOR is (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET);
type TRAFFIC_LIGHT_COLOR is (RED, AMBER, GREEN);
RC : RAINBOW_COLOR;
TC : TRAFFIC_LIGHT_COLOR;
With the above declarations, RC could have any of 7 values, and TC could have
any of 3 values. The compiler will have no trouble compiling
RC := RED;
TC := RED;
because it knows that in RC := RED; RED must be the RAINBOW_COLOR if it's
stored into RC, and in TC := RED; RED must be the TRAFFIC_LIGHT_COLOR if it's
stored into TC. The compiler knows that the types across := must always match.
Naturally, it's illegal to write RC := 2; because of the mixed types.
Also, if we have a procedure DISPLAY that takes one argument of type
RAINBOW_COLOR, the compiler could handle DISPLAY(RED); because it knows that
RED must be the RAINBOW_COLOR to fit the procedure.
1HPlease type a space to go on, or B to go back. 3444 193B191$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type RAINBOW_COLOR is (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET);
type TRAFFIC_LIGHT_COLOR is (RED, AMBER, GREEN);
However, if we had a procedure DISPLAY that takes one RAINBOW_COLOR and another
procedure DISPLAY that takes one TRAFFIC_LIGHT_COLOR, then the statement
DISPLAY(RED); would be ambiguous; the compiler couldn't possibly know which
DISPLAY to call. In that case, we could specify the type by writing
RAINBOW_COLOR'(RED). This is called qualifying the name RED; note the ' with
the parentheses. The call would be DISPLAY(RAINBOW_COLOR'(RED));. The
statements DISPLAY(VIOLET); and DISPLAY(AMBER); aren't ambiguous; the compiler
will figure out which DISPLAY to call in these cases.
Declaring an enumeration type not only defines equality for objects of that
type, but also an order for the values. Thus we can check for <, >, <=, etc.
For example, if we declare A, B: RAINBOW_COLOR; and later write, in the
executable region, A := YELLOW; and B := BLUE; then the test if A < B then ...
will turn out to be TRUE. YELLOW is considered less than BLUE.
Input and output of enumeration types can be done by instantiating the generic
package TEXT_IO.ENUMERATION_IO. For example:
with TEXT_IO; use TEXT_IO; ...
package MY_RAINBOW_IO is new ENUMERATION_IO(RAINBOW_COLOR); use MY_RAINBOW_IO;
1HPlease type a space to go on, or B to go back. 2945 194B192$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$There are two enumeration types built into the Ada language:
type BOOLEAN is (FALSE, TRUE);
type CHARACTER is ( {nul}, {soh}, {stx}, ... , ' ', '!', '"', ... ,
'0', '1', '2', ... , 'A', 'B', 'C', ... ,
'a', 'b', 'c', ... , {del} );
Since the above two declarations are built into the Ada language, they
shouldn't be repeated in your programs. Note that type BOOLEAN is just an
enumeration type. The relational operators ( =, >, <=, etc. ) all return
results of type BOOLEAN.
The definition of type CHARACTER can't be completely written out, because there
are 33 unprintable ASCII characters. However, the enumeration type CHARACTER
contains all 128 values, in ASCII character order. On this screen, we've
denoted the unprintable characters with names in braces, and we've also used
"..." for brevity. Note that even if Ada is run on an EBCDIC machine, it's
guaranteed that type CHARACTER contains the 128 ASCII characters in ASCII
character order.
1HPlease type a space to go on, or B to go back. 16664195119621973198B193$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. type COUNT is ("1", "2", "3", "AB", "CD", "EF");
2. type COUNT is ('1', '2', '3', 'AB', 'CD', 'EF');
3. type COUNT is (1, 2, 3, AB, CD, EF);
4. type COUNT is ('1', '2', '3', AB, CD, EF);
Which one of the above type declarations is legal?
1HPlease press 1, 2, 3, or 4, or B to go back. 2119 199B194Q194$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. type COUNT is ("1", "2", "3", "AB", "CD", "EF");
2. type COUNT is ('1', '2', '3', 'AB', 'CD', 'EF');
3. type COUNT is (1, 2, 3, AB, CD, EF);
4. type COUNT is ('1', '2', '3', AB, CD, EF);
You're right! Enumeration types can contain single characters between ' marks,
and Ada identifiers. Number 1 is illegal because it contains STRINGs, number 2
because ' marks may enclose only single characters, and number 3 because an Ada
identifier can't begin with a digit.
1HPlease type a space to go on, or B or Q to go back to the question. 1847 199B194Q194$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. type COUNT is ("1", "2", "3", "AB", "CD", "EF");
2. type COUNT is ('1', '2', '3', 'AB', 'CD', 'EF');
3. type COUNT is (1, 2, 3, AB, CD, EF);
4. type COUNT is ('1', '2', '3', AB, CD, EF);
No, number 1 is illegal because an enumeration type can't contain STRINGs, only
single characters between ' marks, and Ada identifiers.
1HPlease type a space to go on, or B or Q to go back to the question. 1921 199B194Q194$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. type COUNT is ("1", "2", "3", "AB", "CD", "EF");
2. type COUNT is ('1', '2', '3', 'AB', 'CD', 'EF');
3. type COUNT is (1, 2, 3, AB, CD, EF);
4. type COUNT is ('1', '2', '3', AB, CD, EF);
No, number 2 is illegal because only single characters may appear between '
marks. Enumeration types contain single characters between ' marks, and Ada
identifiers.
1HPlease type a space to go on, or B or Q to go back to the question. 1915 199B194Q194$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. type COUNT is ("1", "2", "3", "AB", "CD", "EF");
2. type COUNT is ('1', '2', '3', 'AB', 'CD', 'EF');
3. type COUNT is (1, 2, 3, AB, CD, EF);
4. type COUNT is ('1', '2', '3', AB, CD, EF);
No, number 3 is illegal because an Ada identifier can't begin with a digit.
Enumeration types can contain only single characters between ' marks, and Ada
identifiers.
1HPlease type a space to go on, or B or Q to go back to the question. 2920 200B194$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ SUBTYPES
A subtype does not define a new type, but it imposes a range constraint on
objects of that subtype. For example,
subtype DAY_SUBTYPE is INTEGER range 1 .. 31;
D : DAY_SUBTYPE; -- D can have only INTEGER values from 1 to 31.
type RAINBOW_COLOR is (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET);
subtype BRIGHT_COLOR is RAINBOW_COLOR range ORANGE .. GREEN;
B : BRIGHT_COLOR; -- B can have only RAINBOW_COLOR values from ORANGE to GREEN.
subtype PROBABILITY is FLOAT range 0.0 .. 1.0;
P : PROBABILITY; -- P can have only FLOAT values from 0.0 to 1.0.
Every time the program stores a value into D, B, or P, a check is made to see
if the value is in range. If it isn't, the message CONSTRAINT_ERROR is
printed. Constraint Errors are usually detected at run time.
Since subtypes don't define new types, the type of D is INTEGER, the type of B
is RAINBOW_COLOR, and that of P is FLOAT. Thus, if we write I : INTEGER; we
can write D := D + I; etc., because D and I have the same type.
1HPlease type a space to go on, or B to go back. 2315 201B199$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$We don't have to give the name of the subtype explicitly. If we declare
D : INTEGER range 1 .. 31;
the compiler creates its own name for the subtype internally; we never see it.
This is called an anonymous subtype. The name the compiler creates will be one
that we can't accidentally create ourselves. Perhaps it will contain a control
character or punctuation mark that Ada doesn't allow us to put into an
identifier. The above declaration is equivalent to
subtype (something) is INTEGER range 1 .. 31;
D : (something);
where (something) represents the anonymous subtype that we can't see or write.
1HPlease type a space to go on, or B to go back. 3253 202B200$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ SIMPLE ATTRIBUTES
An attribute consists of apostrophe (called a "tic" mark for short), and the
name of the attribute. Attributes often follow type names. They're something
like functions, except that they usually involve a type. For example:
type RAINBOW_COLOR is (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET);
type TRAFFIC_LIGHT_COLOR is (RED, AMBER, GREEN);
RAINBOW_COLOR'SUCC(GREEN) is BLUE
TRAFFIC_LIGHT_COLOR'PRED(GREEN) is AMBER
The attributes SUCC and PRED stand for successor and predecessor, respectively.
In RAINBOW_COLORs, the successor of GREEN is BLUE, and in TRAFFIC_LIGHT_COLORs,
the predecessor of GREEN is AMBER. Thus we could write R : RAINBOW_COLOR; and
then R := RAINBOW_COLOR'SUCC(GREEN);. You'll get a CONSTRAINT_ERROR if you
try to take the successor of the last item or the predecessor of the first; for
example, taking RAINBOW_COLOR'PRED(RED) will cause an error.
SUCC and PRED work with any discrete type. That means any integer type or
enumeration type. These two attributes aren't particularly useful with integer
types, because we can simply add or subtract 1 instead. So they're usually
used with enumeration types.
1HPlease type a space to go on, or B to go back. 3340 203B201$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type RAINBOW_COLOR is (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET);
type TRAFFIC_LIGHT_COLOR is (RED, AMBER, GREEN);
The attribute POS converts from a discrete type to an integer, and VAL converts
from an integer to a discrete type. Again, the type is usually an enumeration
type, because there's little point converting from integer to integer.
For example, RAINBOW_COLOR'POS(ORANGE) is 1. (The positions are numbered from
zero.) RAINBOW_COLOR'POS(RED) is 0 and RAINBOW_COLOR'POS(VIOLET) is 6.
TRAFFIC_LIGHT_COLOR'POS(GREEN) is 2, but RAINBOW_COLOR'POS(GREEN) is 3.
CHARACTER'POS('A') is 65, because the ASCII value of 'A' is 65, and the type
CHARACTER contains all 128 characters in ASCII order. CHARACTER'POS('0') is
48, because the ASCII value of the character '0' is 48.
VAL converts the other way, so RAINBOW_COLOR'VAL(0) is RED, and
RAINBOW_COLOR'VAL(6) is VIOLET. Taking the RAINBOW_COLOR'VAL of an argument
outside the range 0 .. 6 will raise a CONSTRAINT_ERROR. CHARACTER'VAL(65) is
'A', and CHARACTER'VAL(7) is the "bell" character (control-G). Since BOOLEAN
is an enumeration type, BOOLEAN'VAL(0) is FALSE and BOOLEAN'VAL(1) is TRUE.
1HPlease type a space to go on, or B to go back. 155412042205B202$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type MONTH_TYPE is (JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC);
What is MONTH_TYPE'POS(FEB)?
1. MONTH_TYPE'POS(FEB) is 1.
2. MONTH_TYPE'POS(FEB) is 2.
1HPlease press 1 or 2, or B to go back. 1525 206B203Q203$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type MONTH_TYPE is (JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC);
You're right! The positions are numbered from zero, so FEB is in position 1,
and MONTH_TYPE'POS(FEB) is 1.
1HPlease type a space to go on, or B or Q to go back to the question. 1471 206B203Q203$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type MONTH_TYPE is (JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC);
No, the positions are numbered from zero, so FEB is in position 1, and
MONTH_TYPE'POS(FEB) is 1.
1HPlease type a space to go on, or B or Q to go back to the question. 2730 207B203$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$While POS and VAL convert to and from integers, the attributes IMAGE and VALUE
convert to and from STRINGs. They work with any discrete types, and are useful
with integer types as well as enumeration types. For example,
type RAINBOW_COLOR is (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET);
type TRAFFIC_LIGHT_COLOR is (RED, AMBER, GREEN);
RAINBOW_COLOR'VALUE("RED") is RED
RAINBOW_COLOR'VALUE("yellow") is YELLOW
TRAFFIC_LIGHT_COLOR'IMAGE(AMBER) is "AMBER"
INTEGER'VALUE("123") is 123
INTEGER'IMAGE(123) is " 123"
INTEGER'IMAGE(-123) is "-123"
VALUE will raise a CONSTRAINT_ERROR if the STRING can't be converted to the
discrete type. For example, taking RAINBOW_COLOR'VALUE("CHARTREUSE") or
INTEGER'VALUE("12X3") will normally print CONSTRAINT_ERROR on the terminal and
stop the program.
1HPlease type a space to go on, or B to go back. 3058 208B206$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$For any discrete type, the attributes FIRST and LAST are also available.
type RAINBOW_COLOR is (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET);
type TRAFFIC_LIGHT_COLOR is (RED, AMBER, GREEN);
RAINBOW_COLOR'FIRST is RED
TRAFFIC_LIGHT_COLOR'LAST is GREEN
A program can use these attributes with INTEGER to determine the size of
integers on the host computer. For example, if your PC Ada has 16-bit 2's
complement integers, while a mainframe Ada uses 32-bit 2's complement, then
INTEGER'FIRST is -32_768 on your PC and -2_147_483_648 on the mainframe.
INTEGER'LAST is 32_767 on your PC and 2_147_483_647 on the mainframe.
Most attributes can also be used with subtypes:
subtype DAY_SUBTYPE is INTEGER range 1 .. 31;
DAY_SUBTYPE'FIRST is 1
DAY_SUBTYPE'LAST is 31
There are two subtype definitions involving LAST built into the Ada language:
subtype POSITIVE is INTEGER range 1 .. INTEGER'LAST;
subtype NATURAL is INTEGER range 0 .. INTEGER'LAST;
1HPlease type a space to go on, or B to go back. 2911 209B207$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$More attributes are discussed in the Advanced Topics section. In summary,
discrete type means any integer or enumeration type. (The only integer type
we've learned about so far is the standard INTEGER.)
POS converts from a discrete type to an integer.
VAL converts from an integer to a discrete type.
IMAGE converts from a discrete type to STRING.
VALUE converts from STRING to a discrete type.
FIRST and LAST take no argument and give a discrete type.
SUCC and PRED take a discrete type and give the same discrete type.
Also remember that a type name followed by ' denotes an attribute:
PUT(CHARACTER'VAL(7)); -- beep
A type name followed by ( ) denotes conversion:
G := F + FLOAT(I);
And a type name followed by '( ) denotes qualification:
DISPLAY(RAINBOW_COLOR'(RED));
1HPlease type a space to go on, or B to go back. 184282101211221232114213521462147215B208$$$$$$$$$$$$$$$$$$$$$$$$ 1. RAINBOW_COLOR'FIRST 5. RAINBOW_COLOR'PRED
2. RAINBOW_COLOR'IMAGE 6. RAINBOW_COLOR'SUCC
3. RAINBOW_COLOR'LAST 7. RAINBOW_COLOR'VAL
4. RAINBOW_COLOR'POS 8. RAINBOW_COLOR'VALUE
Which one of the above attributes would you use to convert from the STRING
"Blue" to the RAINBOW_COLOR BLUE?
1HPlease press 1, 2, 3, 4, 5, 6, 7, or 8, or B to go back. 1461 216B209Q209$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$You're right! The attribute VALUE converts from STRING to a discrete type.
RAINBOW_COLOR'VALUE("Blue") is BLUE.
1HPlease type a space to go on, or B or Q to go back to the question. 1613 216B209Q209$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$No, FIRST and LAST take no argument and return a discrete type:
RAINBOW_COLOR'FIRST is RED and RAINBOW_COLOR'LAST is VIOLET.
You want to convert from a STRING argument to a discrete type.
1HPlease type a space to go on, or B or Q to go back to the question. 1520 216B209Q209$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$No, IMAGE converts from a discrete type to STRING:
RAINBOW_COLOR'IMAGE(BLUE) is "BLUE".
You want to convert from STRING to a discrete type.
1HPlease type a space to go on, or B or Q to go back to the question. 1518 216B209Q209$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$No, POS converts from a discrete type to an integer:
RAINBOW_COLOR'POS(BLUE) is 4.
You want to convert from STRING to a discrete type.
1HPlease type a space to go on, or B or Q to go back to the question. 1628 216B209Q209$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$No, PRED and SUCC take a discrete type argument and return the same discrete
type.
RAINBOW_COLOR'PRED(BLUE) is GREEN and RAINBOW_COLOR'SUCC(BLUE) is INDIGO.
You want to convert from STRING to a discrete type.
1HPlease type a space to go on, or B or Q to go back to the question. 1518 216B209Q209$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$No, VAL converts from an integer to a discrete type.
RAINBOW_COLOR'VAL(4) is BLUE.
You want to convert from STRING to a discrete type.
1HPlease type a space to go on, or B or Q to go back to the question. 3322 217B209$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ OPERATORS
The four basic arithmetic operators, +, -, *, and /, can be used with any
integer or floating point type. (We'll learn about fixed point types in the
Advanced Topics section.) The exponentiation operator, **, takes a base of
type INTEGER or FLOAT, and an exponent of type INTEGER. It returns the same
type as the base. Note that FLOAT ** FLOAT doesn't come with Ada. Of course,
a vendor may supply a math package with his Ada compiler to increase its sales
appeal. If the math package includes a function "**" to raise a FLOAT base to
a FLOAT exponent, then our programs can with and use the package and then use
** between two FLOATs. (Similarly, EXP, LOG, and the trig functions don't
come with Ada, but the vendor may supply them in a math package.)
The six relational operators are:
< less than > greater than = equal to
<= less than or equal to >= greater than or equal to /= not equal to
Five of these are the same as in Basic and Pascal, but the Ada symbol for "not
equal to" is /=, because <> means box. The relational operators compare two
objects of the same type, and return a result of type BOOLEAN.
1HPlease type a space to go on, or B to go back. 3439 218B216$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$All of the above operators are used between two objects (e.g., B := A + B;).
Of course, + and - may also be unary operators (B := +A; A := -A;).
The operators and, or, and xor are used between two BOOLEAN objects, and not is
a unary BOOLEAN operator. Naturally, xor is the exclusive or: A xor B is TRUE
if either A or B is TRUE, but not if both are TRUE.
The numeric operator abs takes the absolute value of an INTEGER or FLOAT.
Since abs is a unary operator like -, we can write B := abs A;. We may, but
don't have to, write B := abs(A);.
When used between two integers, / gives only the integer part of the quotient.
Thus 9/4 is 2. The operators mod (modulo) and rem (remainder) are very similar
to each other, and are used between two integers to give only the remainder in
division. For example, 9 mod 4 is 1. For positive arguments, mod and rem are
the same. But with negative arguments, mod gives the sign of the denominator
while rem gives the sign of the numerator. Many programmers simply ignore rem
and use mod exclusively.
The operator & concatenates arrays, so we'll discuss it later. We'll learn
that a STRING is one type of array (an array of CHARACTERs), so & can
concatenate STRINGs.
1HPlease type a space to go on, or B to go back. 2325 219B217$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ RANGE TESTS
The reserved word in tests if something is inside a range, and returns a
BOOLEAN result. For example, if LOWER, UPPER, and X are declared as FLOATs, we
need not write
if X >= LOWER and X <= UPPER then ...
This can be expressed more clearly as
if X in LOWER .. UPPER then ...
We can also write not in. Thus X not in LOWER .. UPPER is a clear way of
expressing X < LOWER or X > UPPER.
A subtype may be substituted for the range to the right of in. If DAY_SUBTYPE
is defined as a subtype of INTEGER, and D is an INTEGER, then we can write
D in DAY_SUBTYPE.
1HPlease type a space to go on, or B to go back. 1728122022213222B218$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$If we have the following declarations:
B : BOOLEAN;
A, X, Y : CHARACTER;
then which one of the following is illegal?
1. B := X <> Y;
2. B := A in X .. Y;
3. B := A = X and X = Y;
1HPlease press 1, 2, or 3, or B to go back. 1746 223B219Q219$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ B : BOOLEAN;
A, X, Y : CHARACTER;
1. B := X <> Y;
2. B := A in X .. Y;
3. B := A = X and X = Y;
You're right! Number 1 is illegal because <> should be /=.
1HPlease type a space to go on, or B or Q to go back to the question. 1845 223B219Q219$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ B : BOOLEAN;
A, X, Y : CHARACTER;
1. B := X <> Y;
2. B := A in X .. Y;
3. B := A = X and X = Y;
No, number 2 is legal. The reserved word in tests to see if A is within the
range X to Y. It returns a BOOLEAN result, which is stored in B.
1HPlease type a space to go on, or B or Q to go back to the question. 2137 223B219Q219$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ B : BOOLEAN;
A, X, Y : FLOAT;
1. B := X <> Y;
2. B := A in X .. Y;
3. B := A = X and X = Y;
No, number 3 is legal. Parentheses aren't necessary here, but it's the same as
if we had written
B := (A = X) and (X = Y);
In two places = returns a BOOLEAN result. Then and takes the two BOOLEAN
results and also returns a result of type BOOLEAN. That final result is then
stored in B.
1HPlease type a space to go on, or B or Q to go back to the question. 2667 224B219$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ THE SHORT CIRCUIT FORMS
Suppose that N (for numerator) and D (for denominator) are FLOATs, and we want
to execute a block of code if the quotient is 10.0 or greater. We could write
if N/D >= 10.0 then
-----;
-----; (block of code)
-----;
end if;
However, we realize that if D is 0.0, the program will raise a NUMERIC_ERROR
trying to compute N/D. If the denominator is zero, we consider the quotient to
be very large, so we want to execute the block of code when D is zero. Our
second attempt might be
if D = 0.0 or N/D >= 10.0 then
-----;
-----; (block of code)
-----;
end if;
1HPlease type a space to go on, or B to go back. 2873 225B223$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ if D = 0.0 or N/D >= 10.0 then
-----;
-----; (block of code)
-----;
end if;
Here we hope that N/D won't be evaluated if D is zero. We figure that the
compiler should be smart enough to know that if the expression before or is
TRUE, then the whole expression must be TRUE, so the expression to the right of
or needn't be evaluated. However, there's no guarantee that the compiler will
write code to skip the evaluation of the second expression when the first is
TRUE. An optimizing compiler just might, for some unknown reason, even decide
that the expression on the right should be evaluated first.
However, Ada has a "short circuit" form called or else, that looks like this:
if D = 0.0 or else N/D >= 10.0 then
-----;
-----; (block of code)
-----;
end if;
1HPlease type a space to go on, or B to go back. 3330 226B224$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ if D = 0.0 or else N/D >= 10.0 then
-----;
-----; (block of code)
-----;
end if;
Ada guarantees that the expression to the left of or else will be evaluated
first. If this expression is TRUE, the entire expression must be TRUE, and
it's guaranteed that the second expression won't be evaluated. If the first
expression is FALSE, then of course the second expression must be evaluated to
determine if the entire expression is TRUE. Note that the code above will
never try to divide by zero. If D is zero, the expression on the left is TRUE.
The expression on the right won't be evaluated in that case.
There's another "short circuit" form called and then. If the expression to
the left of and then is FALSE, the whole expression must be FALSE, and the
expression on the right won't be evaluated. If the expression on the left is
TRUE, then the expression on the right must be evaluated to determine the value
of the entire expression. Suppose this time that we want to execute a block of
code if N/D is less than 10.0. If the denominator is zero, we consider the
quotient to be very large, and we don't want to execute the block of code in
that case. We can write the following:
1HPlease type a space to go on, or B to go back. 2639 227B225$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ if D /= 0.0 and then N/D < 10.0 then
-----;
-----; (block of code)
-----;
end if;
Again this protects us from trying to divide by zero. If D is zero, the
expression on the left is FALSE. The second expression won't be evaluated in
that case.
Don't use the short circuit forms where the standard and and or will do. The
short circuit forms prevent certain compiler optimizations by requiring the
expression on the left to be evaluated first.
After we discuss arrays, we'll see how the short circuit forms can prevent us
from using out-of-range subscripts. And when we learn about access types,
we'll see how the short circuit forms can keep us from dereferencing a null
pointer. That means trying to access "the object pointed to" when there's no
such object.
1HPlease type a space to go on, or B to go back. 184912282229B226$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Assume that N has been declared FLOAT, and that our program withs and uses a
math package with a square root function called SQRT. Naturally, we don't want
to call SQRT with a negative argument. Which of the following would be more
appropriate?
1. if N >= 0.0 and then SQRT(N) > 1.618 then ...
2. if N >= 0.0 or else SQRT(N) > 1.618 then ...
1HPlease press 1 or 2, or B to go back. 1644 230B227Q227$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. if N >= 0.0 and then SQRT(N) > 1.618 then ...
2. if N >= 0.0 or else SQRT(N) > 1.618 then ...
You're right! We don't want the second expression to be evaluated if the first
expression is FALSE, so we use and then.
1HPlease type a space to go on, or B or Q to go back to the question. 2055 230B227Q227$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. if N >= 0.0 and then SQRT(N) > 1.618 then ...
2. if N >= 0.0 or else SQRT(N) > 1.618 then ...
No, we don't want the second expression to be evaluated if the first expression
is FALSE. The form or else bypasses the evaluation of the second expression
when the first expression is TRUE. Note that if A is true, we know the value
of A or B without evaluating B. Using or else in place of or guarantees that B
won't be evaluated when A is TRUE.
1HPlease type a space to go on, or B or Q to go back to the question. 2841 231B227$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ CONTROL CONSTRUCTS
You're almost ready to write your own Ada program in an Outside Assignment, but
first we need to discuss the control constructs.
Ada encourages structured programming by providing control constructs such as
block ifs with elsifs and else
while loops
for loops
case statements
Ada also provides a goto statement, but it's seldom if ever needed. That's
because the constructs above handle the flow of control more clearly, making
the program easier to read. With the constructs above, we know how control
reaches each statement. When gotos are used, there can be many paths to a
statement, and we're not sure how control got there.
Let's look at the above control constructs, and the goto statement, one at a
time. Discussion of the declare statement and Ada "blocks" is postponed until
we cover exception handlers.
1HPlease type a space to go on, or B to go back. 3256 232B230$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ THE "IF" BLOCK
if A >= B and C = A + D then
-----;
-----; (block of code)
-----;
end if;
In Ada, every if introduces a block of code that ends with end if;. There are
no exceptions; every if always has an end if;. The condition is followed by
then, and it can be any expression with a BOOLEAN result. The above example
is valid if A, B, C, and D are suitably declared. Note that each statement in
the block of code, including the last, has a semicolon. Note also that end if;
is two reserved words, so there must be at least one space between them. All
Ada control constructs, including if blocks, can be nested to any depth.
In Pascal, if is designed to execute only one statement conditionally. If
there's a whole block of statements to be executed conditionally, it has to be
enclosed with "begin ... end" so it will be considered as one statement. In
Ada, however, if always starts a block of code, so "begin ... end" isn't
necessary. The end of the block is always marked by end if; even if there's
just one statement in the block.
1HPlease type a space to go on, or B to go back. 3152 233B231$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ if A >= B and C = A + D then
-----;
-----;
-----;
elsif G = H + P then
-----;
elsif Q > R or S <= T then
-----;
-----;
else
-----;
end if;
The reserved words elsif and else are also available; note the unusual spelling
of elsif. In the above example, only one of the four blocks of code will be
executed. If A >= B and C = A + D is TRUE, the first block of code will be
executed and the remaining tests and blocks will be skipped. Control will
continue after the end if;. If A >= B and C = A + D is FALSE, then G = H + P
will be tested. If it's TRUE, the second block, and only that block, will be
executed; otherwise, Q > R or S <= T will be tested. If that's TRUE, the third
block will be executed; otherwise the else block will be executed. If all the
tests are FALSE and there's no else block, then no blocks are executed.
1HPlease type a space to go on, or B to go back. 3423 234B232$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$if A >= B and C = A + D then if A >= B and C = A + D then
-----; -----;
-----; -----;
-----; THESE -----;
elsif G = H + P then <= TWO => else
-----; ARE if G = H + P then
elsif Q > R or S <= T then EQUIVALENT! -----;
-----; else
-----; if Q > R or S <= T then
else -----;
-----; -----;
end if; else
-----;
end if;
end if;
end if;
As shown, elsif is equivalent to else plus if ... end if;. Although the two
program segments above are equivalent, the one on the left is much clearer.
The example on the left doesn't require multiple end if;s at the bottom, and
the indentation emphasizes that only one of the four blocks of code will be
executed.
1HPlease type a space to go on, or B to go back. 244222351236323742385239B233$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ D, N : INTEGER := 1;
...
if D = 0 then
ONE;
elsif N/D = 1 then
TWO;
elsif D = 1 then
THREE;
else
FOUR;
end if;
In this segment of code, which procedure(s) will be called?
1. Procedure ONE will be called.
2. Procedure TWO will be called.
3. Procedure THREE will be called.
4. Procedure FOUR will be called.
5. Procedures TWO and THREE will be called.
1HPlease press 1, 2, 3, 4, or 5, or B to go back. 2311 240B234Q234$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ D, N : INTEGER := 1;
...
if D = 0 then
ONE;
elsif N/D = 1 then
TWO;
elsif D = 1 then
THREE;
else
FOUR;
end if;
You're right! The first test, D = 0, is FALSE, so the second test is made.
N/D = 1 is TRUE, so TWO is called. Since only one block of code is executed in
an if block, the remaining tests are not done.
1HPlease type a space to go on, or B or Q to go back to the question. 2230 240B234Q234$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ D, N : INTEGER := 1;
...
if D = 0 then
ONE;
elsif N/D = 1 then
TWO;
elsif D = 1 then
THREE;
else
FOUR;
end if;
No, both D and N are initialized to 1, so the first test, D = 0, is FALSE and
the second test is made. The block that's executed is the first block
following a test that's TRUE.
1HPlease type a space to go on, or B or Q to go back to the question. 2413 240B234Q234$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ D, N : INTEGER := 1;
...
if D = 0 then
ONE;
elsif N/D = 1 then
TWO;
elsif D = 1 then
THREE;
else
FOUR;
end if;
No, both D and N are initialized to 1, so the second test, N/D = 1, is TRUE.
The block that's executed is the first block following a test that's TRUE.
Once a block is executed, no further tests in that if block are made. Thus,
THREE isn't called even though D = 1 is TRUE.
1HPlease type a space to go on, or B or Q to go back to the question. 2271 240B234Q234$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ D, N : INTEGER := 1;
...
if D = 0 then
ONE;
elsif N/D = 1 then
TWO;
elsif D = 1 then
THREE;
else
FOUR;
end if;
No, both D and N are initialized to 1, so the second test, N/D = 1, is TRUE.
The block that's executed is the first block following a test that's TRUE. The
else block is executed only if all the tests are FALSE.
1HPlease type a space to go on, or B or Q to go back to the question. 2245 240B234Q234$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ D, N : INTEGER := 1;
...
if D = 0 then
ONE;
elsif N/D = 1 then
TWO;
elsif D = 1 then
THREE;
else
FOUR;
end if;
No, although N/D = 1 and D = 1 are both TRUE, only one block of code in an if
block is executed. The block that's executed is the first block following a
test that's TRUE.
1HPlease type a space to go on, or B or Q to go back to the question. 2512 241B234$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ "WHILE" LOOPS
while I < 10 loop
-----;
-----;
-----;
end loop;
The while loop first evaluates the BOOLEAN expression (I < 10 in this example).
If it's FALSE, the block of code isn't executed at all, and execution continues
after the end loop;. If it's TRUE, the block of code is executed and then the
BOOLEAN condition is again evaluated. If the condition is still TRUE, the
block is executed again and the BOOLEAN expression is evaluated again, and so
forth. When the BOOLEAN expression becomes FALSE, the block of code is skipped
and execution continues after the end loop;.
1HPlease type a space to go on, or B to go back. 2120 242B240$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$To create an "infinite loop," it isn't necessary to write while TRUE loop. We
simply write loop, as follows:
loop
-----;
-----;
-----;
end loop;
Ada doesn't have a "repeat ... until" loop, with the test at the bottom.
However, we can create a loop that's equivalent to a "repeat ... until" loop by
using an infinite loop with an exit statement, to be covered later.
1HPlease type a space to go on, or B to go back. 3273 243B241$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ "FOR" LOOPS
for IX in 1 .. 10 loop for IX in reverse 1 .. 10 loop
-----; -----;
-----; -----;
end loop; end loop;
Here are two sample for loops. We need not declare the index (IX in these
examples) in the declarative region of the program; an index of a for loop
declares itself. In these examples, IX comes into existence at the for
statement and goes out of existence at end loop; the index isn't available
outside the loop. If the name IX happens to be used in the declarative region,
it's a different IX. Inside the loop IX refers to the index; there the name IX
is said to hide any IX that might be mentioned in the declarative region.
The index variable may not be modified. In these examples, IX may not appear
on the left side of an assignment statement, etc.
In the first example above, the block of statements is executed ten times, with
IX equal to 1, then 2, then 3, etc., up to 10. In the second example, IX
starts at 10 and counts down to 1. Note that the range is still written
1 .. 10, but the keyword reverse precedes the range.
1HPlease type a space to go on, or B to go back. 3345 244B242$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$The index of a for loop can have any discrete type (integer or enumeration
type). It can't be of type FLOAT. For example, if we write
type RAINBOW_COLOR is (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET);
type TRAFFIC_LIGHT_COLOR is (RED, AMBER, GREEN);
then we can write for IX in RED .. BLUE loop, and the compiler will know that
IX must be of type RAINBOW_COLOR. Similarly, the type of IX will be
TRAFFIC_LIGHT_COLOR if we write for IX in RED .. AMBER loop. But the compiler
can't handle for IX in RED .. GREEN loop because of the ambiguity. In this
case we have to specify the type. For example, we could write either of these:
for IX in RAINBOW_COLOR'(RED) .. GREEN loop
for IX in RAINBOW_COLOR range RED .. GREEN loop
Because the index can have any discrete type, there's no STEP clause in Ada.
One might increment an INTEGER index by 2, but we couldn't add 2 to RED. For
uniformity, no STEP clause is available, even if the index type is INTEGER.
Ranges may contain expressions. If A, B, C, and D are INTEGER variables, we
can say for I in A + B .. C + D loop or for I in reverse A + B .. C + D loop.
In both cases the range is null if C + D is less than A + B. That won't cause
an error; the loop is simply skipped when the range is null.
1HPlease type a space to go on, or B to go back. 1713224512463247B243$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$If we have
type RAINBOW_COLOR is (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET);
J : INTEGER := 5;
which one of these statements is illegal?
1. for I in -J .. J loop
2. for I in J .. VIOLET loop
3. for I in VIOLET .. RED loop
1HPlease press 1, 2, or 3, or B to go back. 1845 248B244Q244$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type RAINBOW_COLOR is (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET);
J : INTEGER := 5;
1. for I in -J .. J loop
2. for I in J .. VIOLET loop
3. for I in VIOLET .. RED loop
You're right! Number 2 is illegal because the expressions before and after the
".." must be of the same type.
1HPlease type a space to go on, or B or Q to go back to the question. 1872 248B244Q244$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type RAINBOW_COLOR is (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET);
J : INTEGER := 5;
1. for I in -J .. J loop
2. for I in J .. VIOLET loop
3. for I in VIOLET .. RED loop
No, number 1 is legal. The expressions on both sides of .. have the same type.
I will be of type INTEGER and will take 11 values, from -5 to 5.
1HPlease type a space to go on, or B or Q to go back to the question. 2147 248B244Q244$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type RAINBOW_COLOR is (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET);
J : INTEGER := 5;
1. for I in -J .. J loop
2. for I in J .. VIOLET loop
3. for I in VIOLET .. RED loop
No, number 3 is legal. The expressions on both sides of .. have the same type.
The loop will be skipped because RED is less than VIOLET, giving a null range.
But the syntax is legal. To make I start at VIOLET and count down to RED, we
would write
for I in reverse RED .. VIOLET loop
1HPlease type a space to go on, or B or Q to go back to the question. 3133 249B244$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ THE "EXIT" STATEMENT
for I in 1 .. 10 loop for I in 1 .. 10 loop
J := 0; J := 0;
loop loop
J := J + 1; J := J + 1;
if A(I, J) = 0 then
exit; exit when A(I, J) = 0;
end if;
end loop; end loop;
B(I) := J; B(I) := J;
end loop; end loop;
The statement exit; exits the innermost loop. In the example at the left,
exit; transfers control to the statement B(I) := J;. Since exit; is often the
only statement in an if block, exit when is available to abbreviate an if block
with exit; as its only statement. The two examples above are equivalent.
Although exit; can appear anywhere inside any kind of loop, well-structured
Ada programs use only exit when, and then only as the last statement of an
otherwise infinite loop, as above. This simulates a "repeat ... until" loop,
which isn't directly available in Ada.
1HPlease type a space to go on, or B to go back. 3113 250B248$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ OUTER:
for I in 1 .. 10 loop
J := 0;
loop
J := J + 1;
exit OUTER when A(I, J) = 0;
end loop;
B(I) := J;
end loop OUTER;
K := J;
An exit statement can exit other than the innermost loop by naming the loop to
be exited. In this example, the for loop is named OUTER. A loop name is any
Ada identifier, and is followed by a colon. Of course, OUTER: could have been
placed on the same line as the for statement. When a loop is named, the end
loop statement must repeat the name of the loop. The exit statement can then
name the loop to be exited, as in exit OUTER; or exit OUTER when A(I, J) = 0;.
In this example, the exit statement transfers control to the statement K := J;,
not to B(I) := J;.
Well-structured programs don't use exit to leave any loop except the innermost
loop. The above example isn't well-structured.
1HPlease type a space to go on, or B to go back. 2750 251B249$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ LABELS AND GOTOS
OUTER:
for I in 1 .. 10 loop
J := 0;
loop
J := J + 1;
exit OUTER when A(I, J) = 0;
end loop;
B(I) := J;
end loop OUTER;
K := J;
Although an Ada loop name looks exactly like a Pascal label, OUTER: is not a
label, and the program can't goto OUTER. An Ada label looks like this:
<< JAIL >>
The << ... >> makes the label very conspicuous, whether it's on the same line
as a statement or on a line by itself. The program can then say goto JAIL;.
Like all reserved words, goto can't contain a space. Well-structured Ada
programs don't need gotos and labels, so their use is very rare.
1HPlease type a space to go on, or B to go back. 2226125222533254B250$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ I_LOOP: for I in 1 .. 10 loop
J := 0;
J_LOOP: loop
J := J + 1;
exit;
end loop J_LOOP;
L := J;
end loop I_LOOP;
M := J;
In the program segment above, which statement will be executed after exit;?
1. L := J;
2. M := J;
3. for I in 1 .. 10 loop
1HPlease press 1, 2, or 3, or B to go back. 2139 255B251Q251$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ I_LOOP: for I in 1 .. 10 loop
J := 0;
J_LOOP: loop
J := J + 1;
exit;
end loop J_LOOP;
L := J;
end loop I_LOOP;
M := J;
You're right! When used without a name, exit; leaves the innermost loop, so
the next statement executed is L := J;.
1HPlease type a space to go on, or B or Q to go back to the question. 2034 255B251Q251$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ I_LOOP: for I in 1 .. 10 loop
J := 0;
J_LOOP: loop
J := J + 1;
exit;
end loop J_LOOP;
L := J;
end loop I_LOOP;
M := J;
No, when used without a name, exit; leaves the innermost loop.
1HPlease type a space to go on, or B or Q to go back to the question. 2159 255B251Q251$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ I_LOOP: for I in 1 .. 10 loop
J := 0;
J_LOOP: loop
J := J + 1;
exit;
end loop J_LOOP;
L := J;
end loop I_LOOP;
M := J;
No, when used without a name, exit; leaves the innermost loop. The next
statement executed will be the first statement after the end of the innermost
loop.
1HPlease type a space to go on, or B or Q to go back to the question. 3713 256B251$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ THE "CASE" CONSTRUCT
case C is In this example, assume that C is of type
when '*' => CHARACTER. The expression being tested (C),
-----; must have a discrete type: integer or
-----; enumeration type. The case construct must
when '#' | '$' => handle all possible values, but when others is
-----; available. If no action is desired for
-----; characters not mentioned, we can write when
-----;others => null;. The Ada statement null; does
when '0' .. '9' => nothing. Note that the vertical bar can be
-----; used to apply several cases to one block of
when 'A'..'Z' | 'a'..'z' => code, and that ranges can be used. Here, if C
-----; is '*', the first block is executed; if it's
-----; '#' or '$', the second block is executed. If
-----; it's a digit, the third block is executed, and
when others => if it's a letter (upper or lower case), the
-----; fourth block is executed. If C is anything
-----; else, the fifth (last) block of code is
end case; executed.
1HPlease type a space to go on, or B to go back. 2758 257B255$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$case C is
when '*' => if C = '*' then
-----; -----;
-----; -----;
when '#' | '$' => elsif C = '#' or C = '$' then
-----; -----;
-----; -----;
-----; -----;
when '0' .. '9' => elsif C in '0' .. '9' then
-----; -----;
when 'A'..'Z' | 'a'..'z' => elsif C in 'A'..'Z' or C in 'a'..'z' then
-----; -----;
-----; -----;
-----; -----;
when others => else
-----; -----;
-----; -----;
end case; end if;
Note that these two examples are equivalent. In both cases only one block of
code will be executed.
1HPlease type a space to go on, or B to go back. 2469325812592260B256$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$If we have
type RAINBOW_COLOR is (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET);
type TRAFFIC_LIGHT_COLOR is (RED, AMBER, GREEN);
F : FLOAT range 0.0 .. 2.0;
R : RAINBOW_COLOR;
T : TRAFFIC_LIGHT_COLOR;
then which one of the following program segments is legal?
1: 2: 3:
case R is case F is case T is
when RED | GREEN => when 0.0 .. 1.0 => when RED =>
-----; -----; -----;
when BLUE .. VIOLET => when others => when others =>
-----; -----; -----;
end case; end case; end case;
1HPlease press 1, 2, or 3, or B to go back. 2741 261B257Q257$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type RAINBOW_COLOR is (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET);
type TRAFFIC_LIGHT_COLOR is (RED, AMBER, GREEN);
F : FLOAT range 0.0 .. 2.0;
R : RAINBOW_COLOR;
T : TRAFFIC_LIGHT_COLOR;
1: 2: 3:
case R is case F is case T is
when RED | GREEN => when 0.0 .. 1.0 => when RED =>
-----; -----; -----;
when BLUE .. VIOLET => when others => when others =>
-----; -----; -----;
end case; end case; end case;
You're right! Number 1 is illegal because it doesn't account for all possible
values of R, and number 2 is illegal because F doesn't have a discrete type.
1HPlease type a space to go on, or B or Q to go back to the question. 2535 261B257Q257$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type RAINBOW_COLOR is (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET);
type TRAFFIC_LIGHT_COLOR is (RED, AMBER, GREEN);
F : FLOAT range 0.0 .. 2.0;
R : RAINBOW_COLOR;
T : TRAFFIC_LIGHT_COLOR;
1: 2: 3:
case R is case F is case T is
when RED | GREEN => when 0.0 .. 1.0 => when RED =>
-----; -----; -----;
when BLUE .. VIOLET => when others => when others =>
-----; -----; -----;
end case; end case; end case;
No, number 1 is illegal because it doesn't account for all the possible values
of R.
1HPlease type a space to go on, or B or Q to go back to the question. 2513 261B257Q257$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type RAINBOW_COLOR is (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET);
type TRAFFIC_LIGHT_COLOR is (RED, AMBER, GREEN);
F : FLOAT range 0.0 .. 2.0;
R : RAINBOW_COLOR;
T : TRAFFIC_LIGHT_COLOR;
1: 2: 3:
case R is case F is case T is
when RED | GREEN => when 0.0 .. 1.0 => when RED =>
-----; -----; -----;
when BLUE .. VIOLET => when others => when others =>
-----; -----; -----;
end case; end case; end case;
No, number 2 is illegal because F doesn't have a discrete type.
1HPlease type a space to go on, or B or Q to go back to the question. 2856 262B257$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ BRIEF OVERVIEW OF FUNCTIONS
procedure FUNCT_DEMO is
X : FLOAT := 1.2;
Y : FLOAT;
function TWICE(DUMMY : in FLOAT) return FLOAT is
ANSWER : FLOAT;
begin
ANSWER := DUMMY * 2.0;
return ANSWER;
end TWICE;
begin
Y := TWICE(X);
end FUNCT_DEMO;
Later we'll cover functions and procedures in detail, but let's look briefly at
functions now so we can do Outside Assignment 2. Here procedure FUNCT_DEMO
locally declares a function TWICE. TWICE can be called only from within
FUNCT_DEMO: Y := TWICE(X);. Note that the function specification gives the
name (DUMMY), mode (in) and type (FLOAT) of any dummy arguments, or parameters.
For functions, the mode of all parameters must be in, never out or in out. The
function specification also gives the type being returned, in this case FLOAT.
1HPlease type a space to go on, or B to go back. 2628 263B261$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ procedure FUNCT_DEMO is
X : FLOAT := 1.2;
Y : FLOAT;
function TWICE(DUMMY : in FLOAT) return FLOAT is
ANSWER : FLOAT;
begin
ANSWER := DUMMY * 2.0;
return ANSWER;
end TWICE;
begin
Y := TWICE(X);
end FUNCT_DEMO;
The function body must have return followed by an expression of the right type.
In well-structured functions, this is the last statement before end. We don't
return a value by placing the name of the function in an assignment statement,
as in Fortran. Note that function TWICE could be written more simply as
function TWICE(DUMMY : in FLOAT) return FLOAT is
begin
return DUMMY * 2.0;
end TWICE;
1HPlease type a space to go on, or B to go back. 2819 264B262$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ procedure FUNCT_DEMO is
X : FLOAT := 1.2;
Y : FLOAT;
function TWICE(DUMMY : in FLOAT) return FLOAT is separate;
begin
Y := TWICE(X);
end FUNCT_DEMO;
separate (FUNCT_DEMO)
function TWICE(DUMMY : in FLOAT) return FLOAT is
begin
return DUMMY * 2.0;
end TWICE;
The function can be placed in a separate file and compiled later. Here the
calling program can be compiled once, and later the function can be edited and
recompiled as often as desired. TWICE is still local to FUNCT_DEMO. The
phrase separate (FUNCT_DEMO) tells Ada that this subprogram is part of the
previously compiled unit FUNCT_DEMO. It's as if the function were cut out and
"pasted" where the calling program says function TWICE ... is separate;. Ada
can still compile the call Y := TWICE(X); because the calling program contains
the specification of the function.
1HPlease type a space to go on, or B to go back. 3271 265B263$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ OUTSIDE ASSIGNMENT 2 - EXERCISE IN ENUMERATION TYPES
Separate compilation is the basis of our next Outside Assignment. The function
we want you to write is completely specified by these two lines of Ada:
type TRIANGLE is (EQUILATERAL, ISOSCELES, SCALENE, NOT_A_TRIANGLE);
function TRITYPE(LEN1, LEN2, LEN3 : in INTEGER) return TRIANGLE;
From these two lines, it's obvious that the function must decide whether three
integers, representing the lengths of the sides of a triangle, in fact
represent an equilateral triangle, an isosceles triangle, a scalene triangle,
or no triangle at all. (An equilateral triangle has three equal sides; an
isosceles triangle has only two equal sides. A scalene triangle has sides of
three different lengths, and the integers -1, 0, 5 represent no triangle.)
A test driver for your function is already written, and is in the file
TRITEST.ADA. A listing starts on page 9 of your printed course notes. You
need not understand the test driver. It's sufficient to know that if your
function passes all the tests, the message "Congratulations, you completed the
assignment!" is shown. For any failed tests, the test driver displays the test
case, the answer from your function, and the correct answer. This will help
you in debugging your function.
1HPlease type a space to go on, or B to go back. 2765 266B264$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Note that the test driver, which is the main program, contains
type TRIANGLE is (EQUILATERAL, ISOSCELES, SCALENE, NOT_A_TRIANGLE);
function TRITYPE(LEN1, LEN2, LEN3 : in INTEGER) return TRIANGLE is separate;
This allows you to edit and recompile your function as often as you like,
without having to recompile the test driver. Since type TRIANGLE is defined in
the calling program, you should not define it in your function.
To get you started, a dummy solution is given in TRITYPE.DUM. You can copy it
and edit the copy to become your real solution. It looks like this:
-- Dummy solution for Outside Assignment 2
-- Edit to become your real solution.
separate (TRITEST)
function TRITYPE(LEN1, LEN2, LEN3 : in INTEGER) return TRIANGLE is
begin
return NOT_A_TRIANGLE;
end TRITYPE;
If you wish, you can compile this dummy solution before editing it. Then
you'll see what error messages from the test driver look like.
1HPlease type a space to go on, or B to go back. 2570 267B265$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ -- Dummy solution for Outside Assignment 2
-- Edit to become your real solution.
separate (TRITEST)
function TRITYPE(LEN1, LEN2, LEN3 : in INTEGER) return TRIANGLE is
begin
return NOT_A_TRIANGLE;
end TRITYPE;
When you do edit the function, you'll probably want to remove or change the
first two comment lines. Don't change the lines highlighted above. You may
want to create a new variable of type TRIANGLE, and return that variable
instead of the constant NOT_A_TRIANGLE at the end of your function:
separate (TRITEST)
function TRITYPE(LEN1, LEN2, LEN3 : in INTEGER) return TRIANGLE is
ANSWER : TRIANGLE;
begin
if ... end if;
return ANSWER;
end TRITYPE;
However, that's not the only way to solve the problem, just a suggestion.
1HPlease type a space to go on, or B to go back. 2453 268B266$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Here are the steps to follow for Outside Assignment 2. They're also in your
printed course notes on page 11:
1. Compile the test driver TRITEST.ADA. Also, make a copy of the dummy
solution by typing COPY TRITYPE.DUM TRITYPE.ADA. You need do this step
only once.
2. Edit TRITYPE.ADA to become your real solution. You can skip this step the
first time through, to see error messages from the test driver.
3. Compile your solution TRITYPE.ADA. If there are compiler errors, go back
to step 2.
4. Link with the name of the main program TRITEST. Then execute. If the test
driver prints error messages, go back to step 2.
5. When the message "Congratulations, you completed the assignment!" is
printed, you'll have a chance to compare your solution with ours.
1HPlease type a space to go on, or B to go back. 2160 269B267$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Remember that a function can't change the values of its parameters (in this
case, LEN1, LEN2, and LEN3), because function parameters are always of mode in.
This assignment should be a simple exercise in enumeration types and the
control constructs. Our solution easily fits on one screen. If you find your
solution getting long and difficult, you should probably think through the
problem again. See if there's a simple way to test for the four possible
answers.
Please type X to exit ADA-TUTR temporarily, and try Outside Assignment 2. Work
at your own pace; there's no deadline. Good luck!
1HPlease type X to exit, a space to go on, or B to go back. 2222 270B268$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Congratulations on Completing Outside Assignment 2!
Our solution is in TRITYPE.ANS. It looks like this:
-- Our solution to Outside Assignment 2:
separate (TRITEST)
function TRITYPE(LEN1, LEN2, LEN3 : in INTEGER) return TRIANGLE is
ANSWER : TRIANGLE;
begin
if LEN1 + LEN2 <= LEN3 or LEN1 + LEN3 <= LEN2 or LEN2 + LEN3 <= LEN1 then
ANSWER := NOT_A_TRIANGLE;
elsif LEN1 = LEN2 and LEN2 = LEN3 then
ANSWER := EQUILATERAL;
elsif LEN1 = LEN2 or LEN2 = LEN3 or LEN1 = LEN3 then
ANSWER := ISOSCELES;
else
ANSWER := SCALENE;
end if;
return ANSWER;
end TRITYPE;
1HPlease type a space to go on, or B to go back. 2670 271B269$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Note that it isn't necessary to include tests for sides <= 0, because if any
side <= 0, one of the three tests in the following would have to catch that:
if LEN1 + LEN2 <= LEN3 or LEN1 + LEN3 <= LEN2 or LEN2 + LEN3 <= LEN1 then
ANSWER := NOT_A_TRIANGLE;
However, most students include tests for sides <= 0, so if you did, don't feel
bad. It doesn't do any harm.
You must have realized by now that it's impossible to draw a triangle with
sides 1, 2, and 3. The sum of any two sides must be greater than the third.
There are many correct ways to solve this problem. Some students sort the
three sides first. Some initialize ANSWER to SCALENE in the declarative region
and omit the code
else
ANSWER := SCALENE;
If you saw the message "Congratulations, you completed the assignment!" you can
consider your solution correct. Let's go on to discuss Records and Arrays.
1HPlease type a space to go on, or B to go back. 2869 272B270$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ RECORDS
A record lets us group several declarations together and refer to them as one:
type MONTH_TYPE is (JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC);
subtype DAY_SUBTYPE is INTEGER range 1 .. 31;
type DATE is
record
DAY : DAY_SUBTYPE; -- Note that DAY_SUBTYPE and
MONTH : MONTH_TYPE; -- MONTH_TYPE must be defined
YEAR : INTEGER; -- before type DATE is defined.
end record;
USA : DATE;
In this example, USA has three parts (called "fields"): USA.DAY, USA.MONTH,
and USA.YEAR. The fields of a record can be of any type, including other
records. Here USA.DAY is of type INTEGER, USA.MONTH is of type MONTH_TYPE, and
USA.YEAR is of type INTEGER. The parts of USA may be used separately:
USA.DAY := 4;
USA.MONTH := JUL;
USA.YEAR := 1776;
1HPlease type a space to go on, or B to go back. 3018 273B271$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type MONTH_TYPE is (JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC);
subtype DAY_SUBTYPE is INTEGER range 1 .. 31;
type DATE is
record
DAY : DAY_SUBTYPE;
MONTH : MONTH_TYPE;
YEAR : INTEGER;
end record;
USA : DATE;
However, USA can also be used as one object. For example, we could have
assigned all three fields in one statement: USA := (4, JUL, 1776);. Here the
object on the left is of type DATE, and the object on the right is called an
aggregate. The aggregate fits the type DATE because it contains an INTEGER, a
MONTH_TYPE, and another INTEGER. This aggregate is said to use positional
notation because its three parts appear in the same order as the three parts of
the record definition: first DAY, then MONTH, then YEAR.
We can specify the parts of an aggregate in any order we like if we use named
notation: USA := (MONTH => JUL, DAY => 4, YEAR => 1776);. The symbol => is
read "arrow." Using named notation often improves program readability,
especially if the names of the fields are well chosen.
1HPlease type a space to go on, or B to go back. 2363 274B272$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type MONTH_TYPE is (JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC);
subtype DAY_SUBTYPE is INTEGER range 1 .. 31;
type DATE is
record
DAY : DAY_SUBTYPE;
MONTH : MONTH_TYPE;
YEAR : INTEGER;
end record;
USA : DATE;
We can switch from positional to named notation in an aggregate. But once we
use named notation, the compiler loses track of position, so we can't switch
back to positional. For example, the following is legal:
USA := (4, YEAR => 1776, MONTH => JUL);
But the following is illegal because positional notation can't follow named in
an aggregate:
USA := (4, YEAR => 1776, JUL); -- illegal
1HPlease type a space to go on, or B to go back. 21743275127622774278B273$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ procedure RECORD_EXERCISE is
type MONTH_TYPE is (JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC);
subtype DAY_SUBTYPE is INTEGER range 1 .. 31;
type DATE is
record
DAY : DAY_SUBTYPE;
MONTH : MONTH_TYPE;
YEAR : INTEGER;
end record;
D1, D2, D3 : DATE; -- 1
begin
D1 := (MONTH => AUG, DAY => 14, YEAR => 1945); -- 2
D2.DAY := 22;
D2.MONTH := 1; -- 3
D2.YEAR := 1983;
D3 := D2; -- 4
end RECORD_EXERCISE;
Which commented line in the above program is illegal?
1HPlease press 1, 2, 3, or 4, or B to go back. 1515 279B274Q274$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ D2.MONTH := 1; -- 3
You're right! This statement is illegal, because the types are mixed.
D2.MONTH is of type MONTH_TYPE, and 1 is an integer.
1HPlease type a space to go on, or B or Q to go back to the question. 1458 279B274Q274$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ D1, D2, D3 : DATE; -- 1
No, this statement is legal, because several objects may be declared in one
statement, if the objects are separated by commas.
1HPlease type a space to go on, or B or Q to go back to the question. 1517 279B274Q274$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ D1 := (MONTH => AUG, DAY => 14, YEAR => 1945); -- 2
No, this statement is legal, because the fields within the aggregate may appear
in any order when named notation is used.
1HPlease type a space to go on, or B or Q to go back to the question. 1474 279B274Q274$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ D3 := D2; -- 4
No, this statement is legal, because D3 and D2 both have the same type: DATE.
An entire record can be assigned with one statement.
1HPlease type a space to go on, or B or Q to go back to the question. 3247 280B274$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ARRAYS
To declare an array in Ada, we specify the type and range of the subscript,
followed by the type of the elements of the array. The subscript can have any
discrete type (integer or enumeration), and the elements of the array can be of
any type at all, including records and other arrays. There are three ways to
declare an array in Ada. Here are three examples of the most direct, but least
flexible, way (types RAINBOW_COLOR and DATE must be defined earlier):
A : array(RAINBOW_COLOR range ORANGE .. BLUE) of DATE;
-- A four-element array, each element of which is a record with three parts.
-- The allowable subscripts are ORANGE, YELLOW, GREEN, and BLUE. Note that
-- A(YELLOW) is of type DATE, and A(YELLOW).YEAR is of type INTEGER.
B : array(INTEGER range -10 .. 10) of INTEGER;
-- An array of 21 INTEGERs, with INTEGER subscripts.
C : array(0 .. 30) of FLOAT;
-- Here (0 .. 30) is understood to mean (INTEGER range 0 .. 30), and we have
-- an array of 31 FLOATs, with INTEGER subscripts.
A subscript can be an expression; if I is an INTEGER, we can write C(2*I). If
a subscript is out-of-range (for example, A(RED) or C(-32)), the program will
raise a CONSTRAINT_ERROR.
1HPlease type a space to go on, or B to go back. 2969 281B279$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$This direct method of declaring arrays is usually used to create single arrays
for table lookup, etc., where there's no need to have several arrays of the
same type. A better way to declare an array is to specify a type name for the
array itself. Then several objects can be declared to have that same type.
For example,
type VECTOR100 is array(1 .. 100) of FLOAT;
type VECTOR300 is array(1 .. 300) of FLOAT;
D, E, F : VECTOR100;
G, H : VECTOR300;
Here D, E, and F are all of type VECTOR100, so we can write D := E; and assign
the entire array with one statement. Similarly, we can write G := H;, but not
G := F;.
Note that the example above takes four statements. An even better way to
declare arrays is to leave the range of the subscript unspecified with the box
symbol, <>, specifying the range when declaring the objects. For example,
type VECTOR is array(INTEGER range <>) of FLOAT;
D, E, F : VECTOR(1 .. 100);
G, H : VECTOR(1 .. 300);
1HPlease type a space to go on, or B to go back. 3023 282B280$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$There are two errors to avoid when declaring arrays in this way. One is to
declare a type with the box symbol for the range of the subscript, and then
fail to specify the range when declaring an object (other than a constant):
type VECTOR is array(INTEGER range <>) of FLOAT;
D1 : VECTOR; -- Illegal
D2 : constant VECTOR := (2.3, 4.5, 4.0); -- Legal
This error is called unconstrained array. Unconstrained arrays are legal in
dummy arguments ("formal parameters") of procedures and functions, and a
function can return an unconstrained array type. (We'll learn about these
things later.) But an unconstrained array is illegal when declaring a
variable, because the compiler needs to know the range of the subscript.
The other error is to specify the range of the subscript twice: once when
declaring the type and once when declaring the object:
type VECTOR100 is array(1 .. 100) of FLOAT;
D2 : VECTOR100(1 .. 100); -- Illegal
Even if the two ranges agree, this is illegal and is called doubly constrained
array.
1HPlease type a space to go on, or B to go back. 2552 283B281$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$In an array declaration, we can totally omit the range of the subscript (not
even supplying a box), if the type or subtype of the subscript has a reasonable
number of possible values. For example, in
type RAINBOW_COLOR is (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET);
R : array(RAINBOW_COLOR) of FLOAT;
we have an array of seven FLOATs, because there are seven possible values of
type RAINBOW_COLOR. Similarly,
V : array(CHARACTER) of BOOLEAN;
creates an array of 128 BOOLEANs. However, J : array(INTEGER) of FLOAT; is an
attempt to declare a very large array indeed! In a case like this, we can use
a subtype in the subscript declaration:
subtype DAY_SUBTYPE is INTEGER range 1 .. 31;
J : array(DAY_SUBTYPE) of FLOAT;
This creates an array of 31 FLOATs.
1HPlease type a space to go on, or B to go back. 3169 284B282$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$The attributes FIRST and LAST, which we've seen with discrete types, can also
be used with array types and with the array names themselves. For example,
type MY_VECTOR is array(30 .. 33) of INTEGER;
MV : MY_VECTOR;
...
MV(30) := 1000;
MV(31) := 20;
MV(32) := -70;
MV(33) := -500;
Here MV'FIRST and MY_VECTOR'FIRST are both 30. MV'LAST and MY_VECTOR'LAST are
both 33. Note that FIRST and LAST refer to the subscripts, not to the values
of the array elements. Thus MV'FIRST is not 1000. To obtain the value of the
first element, we would use MV'FIRST as a subscript. MV(MV'FIRST) is 1000.
The attribute RANGE is an abbreviation for FIRST .. LAST. It can be used only
with array types and array names, not with discrete types. Thus MV'RANGE and
MY_VECTOR'RANGE both mean 30 .. 33. We can't write INTEGER'RANGE, even though
we can write INTEGER'FIRST and INTEGER'LAST. The attribute LENGTH is also
available: MV'LENGTH and MY_VECTOR'LENGTH are both 4.
1HPlease type a space to go on, or B to go back. 2111328512862287B283$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Which one of these is illegal?
1. subtype DAY_SUBTYPE is INTEGER range 1 .. 31;
type GROUP is array(DAY_SUBTYPE) of FLOAT;
GR : GROUP;
...
GR(3) := 0.0;
2. type LINE is array(1 .. 10) of INTEGER;
type PAGE is array(1 .. 20) of LINE;
PG : PAGE;
...
PG(5)(10) := 0;
3. type ROW is array (INTEGER range <>) of INTEGER;
R1 : ROW;
...
R1(1) := 0;
1HPlease press 1, 2, or 3, or B to go back. 1873 288B284Q284$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 3. type ROW is array (INTEGER range <>) of INTEGER;
R1 : ROW;
...
R1(1) := 0;
You're right! The above is illegal because the range of the subscript must be
specified, either in the first line, (INTEGER range 1 .. 10), or in the
second, R1 : ROW(1 .. 10);, but not both. This is the unconstrained array
error mentioned earlier.
1HPlease type a space to go on, or B or Q to go back to the question. 1835 288B284Q284$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. subtype DAY_SUBTYPE is INTEGER range 1 .. 31;
type GROUP is array(DAY_SUBTYPE) of FLOAT;
GP : GROUP;
...
GP(3) := 0.0;
No, the above is legal. GP is an array of 31 elements. The subscript ranges
from 1 to 31, and each element has type FLOAT. Therefore, GP(3) has type
FLOAT, and the assignment is legal.
1HPlease type a space to go on, or B or Q to go back to the question. 2113 288B284Q284$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 2. type LINE is array(1 .. 10) of INTEGER;
type PAGE is array(1 .. 20) of LINE;
PG : PAGE;
...
PG(5)(10) := 0;
No, the above is legal. The elements of an array can be of any type, including
other arrays. Here PG has type PAGE, which is an array of LINE. Therefore
PG(5) has type LINE (which is an array of INTEGER), and PG(5)(10) has type
INTEGER. Thus the assignment is legal. The notation PG(5)(10) may seem
strange, but it's correct Ada when we have an array of arrays.
1HPlease type a space to go on, or B or Q to go back to the question. 2943 289B284$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ MULTIDIMENSIONAL ARRAYS
Ada arrays may have any number of dimensions, and the subscripts may be of
different discrete types. For example, assuming RAINBOW_COLOR, MONTH_TYPE, and
DATE have already been defined, we can write
X : array(INTEGER range -10 .. -1, RAINBOW_COLOR range ORANGE .. BLUE,
MONTH_TYPE range FEB .. JUN) of DATE;
Here the first subscript is of type INTEGER and has 10 possible values, the
second subscript is of type RAINBOW_COLOR and has four possible values, and the
third subscript has type MONTH_TYPE with five possible values. Thus we have
a three-dimensional array of 10 * 4 * 5 = 200 DATEs. One element of the array
might be X(-5, GREEN, APR); one field of that element might be
X(-5, GREEN, APR).YEAR. The array in this example probably has no use, other
than demonstrating that multiple subscripts need not have the same type.
If one subscript of a multidimensional array is constrained, they must all be
constrained. We can't write
X : array(1 .. 5, INTEGER range <>) of FLOAT; -- illegal
1HPlease type a space to go on, or B to go back. 3046 290B288$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Arrays may be initialized and assigned with aggregates, and both positional and
named notation may be used. For example, arrays A and B are equal here:
type VECTOR5 is array(1 .. 5) of FLOAT;
A : constant VECTOR5 := (2.0, 4.0, 8.0, 16.0, 32.0);
Sometimes we must qualify an aggregate when others is used with named notation;
at other times it's optional. The rules (LRM section 4.3.2, paragraphs 4-8)
are very complicated. It's easiest always to qualify an aggregate when others
follows named notation, as shown above.
1HPlease type a space to go on, or B to go back. 2243 291B289$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Multidimensional arrays can be initialized or assigned with nested aggregates.
This example creates a two-dimensional array of FLOATs, initializing all 50
elements to 1.0:
MAT : array(0 .. 9, 1 .. 5) of FLOAT := (others => (others => 1.0));
Here X is a 10-by-10 array of INTEGERs. All elements are 0, except X(4, 5),
which is 1:
type SQUARE10 is array (1 .. 10, 1 .. 10) of INTEGER;
Note that in this second example, we qualified the aggregate because others
follows named notation.
1HPlease type a space to go on, or B to go back. 2964 292B290$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$In array aggregates, multiple choices can be denoted with the vertical bar (|),
shift-backslash on your keyboard. In this array, the elements with odd
subscripts are TRUE, while the elements with even subscripts are FALSE:
1HPlease type a space to go on, or B to go back. 2028 293B291$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$The "short circuit" forms can prevent us from using array subscripts that are
out of range. For example, if we write
A : array(1 .. 10) of FLOAT;
I : INTEGER;
...
if I in A'RANGE and then A(I) = 0.0 then
-----;
-----; (block of code)
-----;
end if;
then we know the program won't try to evaluate A(I) when I is outside the range
1 to 10.
1HPlease type a space to go on, or B to go back. 23491294229532964297B292$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$with TEXT_IO; use TEXT_IO;
procedure ARRAY_QUIZ is
package MY_INT_IO is new INTEGER_IO(INTEGER); use MY_INT_IO;
subtype CAPITAL_LETTER is CHARACTER range 'A' .. 'Z';
type SET_OF_LETTERS is array(CAPITAL_LETTER) of BOOLEAN;
You're right! VOWELS has 26 elements, with subscripts 'A' through 'Z'.
VOWELS(LETTER) is VOWELS('E'), which is TRUE, and since Ada defines
type BOOLEAN is (FALSE, TRUE);
and the positions are numbered starting from 0, BOOLEAN'POS(TRUE) is 1.
1HPlease type a space to go on, or B or Q to go back to the question. 2219 298B293Q293$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$with TEXT_IO; use TEXT_IO;
procedure ARRAY_QUIZ is
package MY_INT_IO is new INTEGER_IO(INTEGER); use MY_INT_IO;
subtype CAPITAL_LETTER is CHARACTER range 'A' .. 'Z';
type SET_OF_LETTERS is array(CAPITAL_LETTER) of BOOLEAN;
and that the positions are numbered starting with 0 for the POS attribute.
1HPlease type a space to go on, or B or Q to go back to the question. 2270 298B293Q293$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$with TEXT_IO; use TEXT_IO;
procedure ARRAY_QUIZ is
package MY_INT_IO is new INTEGER_IO(INTEGER); use MY_INT_IO;
subtype CAPITAL_LETTER is CHARACTER range 'A' .. 'Z';
type SET_OF_LETTERS is array(CAPITAL_LETTER) of BOOLEAN;
No. Indeed VOWELS(LETTER) is VOWELS('E'), which is TRUE, but the program takes
the BOOLEAN'POS of VOWELS(LETTER). Recall that the attribute POS converts from
a discrete type (in this case, BOOLEAN) to an integer.
1HPlease type a space to go on, or B or Q to go back to the question. 2343 298B293Q293$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$with TEXT_IO; use TEXT_IO;
procedure ARRAY_QUIZ is
package MY_INT_IO is new INTEGER_IO(INTEGER); use MY_INT_IO;
subtype CAPITAL_LETTER is CHARACTER range 'A' .. 'Z';
type SET_OF_LETTERS is array(CAPITAL_LETTER) of BOOLEAN;
No. While LETTER is 'E', and LETTER appears inside the PUT statement, the
argument of PUT is the BOOLEAN'POS of one element of array VOWELS
(specifically, the element whose subscript is 'E'). Recall that the POS
attribute always returns an integer.
1HPlease type a space to go on, or B or Q to go back to the question. 3164 299B293$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ STRINGS
There's one very important array type declaration built into the Ada language.
As with types BOOLEAN and CHARACTER, and subtypes POSITIVE and NATURAL, this
definition comes with Ada and shouldn't be repeated in our programs:
type STRING is array(POSITIVE range <>) of CHARACTER;
Thus we can declare, for example, S : STRING(1 .. 5);. We can't simply write
S : STRING; because we can't declare unconstrained arrays. (We can declare
S : constant STRING := "Hello";). Note that STRING isn't a special type in
Ada, it's just an array of CHARACTERs. Everything we learned about arrays
applies to STRINGs. For example, we can assign to S using the same syntax that
we use when assigning to an array of any other type. If we write
S : STRING(1 .. 5); we can write:
S := ('H', 'e', 'l', 'l', 'o');
However, this notation is clumsy, so Ada allows us to abbreviate an array of
CHARACTER constants using the double quote. Thus S := "Hello"; is equivalent
to the statement above. If a quotation mark appears inside the string, it must
be doubled. Thus TEXT_IO.PUT_LINE("a ""big"" man"); will print a "big" man.
1HPlease type a space to go on, or B to go back. 3166 300B298$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$It may seem disappointing that Ada STRINGs have fixed length, and that we can't
declare a variable S : STRING;. Later we'll learn how to define our own type
TEXT to get around this restriction and simulate variable-length STRINGs.
When arrays are assigned, the lengths must be the same on both sides of the :=,
and the types of the elements must be the same, but the subscripts needn't be
the same. For example, if we have
type VECTOR is array(INTEGER range <>) of FLOAT;
V1 : VECTOR(1 .. 5);
V2 : VECTOR(2 .. 6) := (others => 0.0);
S1 : STRING(1 .. 5);
S2 : STRING(2 .. 6) := (others => ' ');
then we can write V1 := V2; and S1 := S2; even though the subscripts are
different, because the array lengths are the same and the element types are the
same. But we'll get a CONSTRAINT_ERROR if we write S1 := "Hello there"; or
S1 := "Hi"; or V1 := (1.0, 2.0, 3.0);, because these arrays have wrong lengths.
Ada won't automatically truncate STRINGs or pad with blanks. Of course, it
would be easy to write our own procedure to assign STRINGs of different
lengths, padding or truncating as necessary.
1HPlease type a space to go on, or B to go back. 3757 301B299$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$A slice of an array is a portion of an array, and is indicated with a range in
the subscript. A slice is itself an array. Some languages use the term
"substring" to refer to a slice of a STRING, but in Ada we can take a slice of
any kind of array, not just an array of CHARACTERs. So instead of "substring,"
Ada uses the more general term "slice." For example, if we have
A : array(1 .. 10) of INTEGER := (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
then A(1 .. 3) is the array (1, 2, 3) and A(6 .. 9) is the array (6, 7, 8, 9).
Similarly, if we have S : STRING(1 .. 11) := "Hello there"; then S(8 .. 11) is
"here" and S(4 .. 5) is "lo". We can also write S(1 .. 10) := S(2 .. 11); and
A(1 .. 3) := A(4 .. 6); since the lengths are the same on both sides.
If the value preceding .. is greater than the value following it, we have a
null range. A slice with a null range has a length of zero, and is called a
null slice. In the case of a null slice, the subscript is not checked for
CONSTRAINT_ERROR. Thus, even if N is 0 we could write S(1 .. N); which would
produce the null string "". This is legal, even though Ada defines
"type STRING is array(POSITIVE range <>) of CHARACTER;". Assigning a null
slice to a null slice does no harm and generates no error; it does nothing.
Also, if S is a null array, then S'LENGTH is 0, and S'FIRST and S'LAST don't
exist. Using FIRST or LAST with a null array will raise a CONSTRAINT_ERROR.
1HPlease type a space to go on, or B to go back. 2537 302B300$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Beginners sometimes confuse a CHARACTER with a STRING of length 1. If we
write
S : STRING(1 .. 10);
I : INTEGER := 5;
then S(I) is a CHARACTER and S(I .. I) is a STRING of length 1. Also, 'X' is a
CHARACTER while "X" is a STRING of length 1. Thus we could write
S(I) := 'X';
S(I .. I) := "X";
but we'd be mixing types if we were to write S(I) := "X"; or S(I .. I) := 'X';.
Fortunately, TEXT_IO has a PUT for type CHARACTER as well as a PUT for type
STRING. (It also has a GET for each of these types.) Thus we can write either
PUT(S(I .. I)); or PUT(S(I));. However, PUT_LINE and GET_LINE exist only for
STRINGs, not for CHARACTERs.
1HPlease type a space to go on, or B to go back. 18244303130423053306530763087309B301$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. HELLO : STRING := "Hello there";
1HPlease press 1, 2, 3, 4, 5, 6, or 7, or B to go back. 2817 310B302Q302$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. HELLO : STRING := "Hello there";
You're right! Number 4 creates HELLO, a STRING of length 5, and initializes it
to "Hello", a STRING of the same length. The subscript of HELLO need not start
at 1, so long as the length is 5.
Number 1 attempts to create an unconstrained array. Number 2 has a zero
subscript, while Ada defines type STRING for POSITIVE subscripts. Number 3
should have '*' instead of "*". Number 5 tries to set each of the first three
elements, which are CHARACTERs, to a STRING. Number 6 tries to store a STRING
of length 1 into a STRING of length 3, and number 7 should have "Hello"
instead of 'Hello'.
1HPlease type a space to go on, or B or Q to go back to the question. 2416 310B302Q302$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. HELLO : STRING := "Hello there";
No, number 1 is illegal because it tries to create an unconstrained array. The
fact that "Hello there" has a definite length doesn't make the statement legal.
We must constrain the STRING to a length of 11 by writing, for example,
HELLO : STRING(1 .. 11) := "Hello there";. However, it is legal to write
HELLO : constant STRING := "Hello there";
1HPlease type a space to go on, or B or Q to go back to the question. 2053 310B302Q302$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. HELLO : STRING := "Hello there";
No, number 2 is illegal because of the zero subscript. Ada defines "type
STRING is array(POSITIVE range <>) of CHARACTER;". Therefore, the subscripts
must be 1 or greater.
1HPlease type a space to go on, or B or Q to go back to the question. 2038 310B302Q302$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. HELLO : STRING := "Hello there";
No, number 3 is illegal because "*" should be '*'. As it stands, it tries to
assign a STRING of length 1 to each of the elements, which are CHARACTERs.
1HPlease type a space to go on, or B or Q to go back to the question. 2436 310B302Q302$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. HELLO : STRING := "Hello there";
No, number 5 is illegal because it tries to initialize each of the first three
elements, which are CHARACTERs, to a STRING of length 3. We could, however,
have written simply HELLO : STRING(1 .. 5);, and then written the following in
the executable region:
HELLO(1 .. 3) := "Hel";
HELLO(4) := 'l';
HELLO(5) := 'o';
1HPlease type a space to go on, or B or Q to go back to the question. 1932 310B302Q302$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. HELLO : STRING := "Hello there";
No, number 6 is illegal because it tries to assign a STRING of length 1 to a
STRING of length 3.
1HPlease type a space to go on, or B or Q to go back to the question. 2162 310B302Q302$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. HELLO : STRING := "Hello there";
No, number 7 is illegal because it should say "Hello" instead of 'Hello'. Ada
"tic" marks (') always enclose a single CHARACTER, while double quotes (")
always enclose an array of CHARACTERs, that is, a STRING.
1HPlease type a space to go on, or B or Q to go back to the question. 2553 311B302$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ARRAY OPERATORS
The operator & concatenates any two arrays of the same element type, including
two STRINGs. It can also concatenate a single element with an array of that
element type, or two single elements into an array of length two. For example,
every use of & below is legal:
C, D : CHARACTER := '*';
S2 : STRING(1 .. 2);
S3 : STRING(1 .. 3) := (others => ' ');
S5 : STRING(1 .. 5);
type VECTOR is array(INTEGER range <>) of FLOAT;
F, G : FLOAT := 1.2;
V2 : VECTOR(1 .. 2);
V3 : VECTOR(1 .. 3) := (others => 0.0);
V5 : VECTOR(1 .. 5);
...
S2 := C & D; S5 := S2 & S3; S3 := C & S2; S3 := S2 & C;
V2 := F & G; V5 := V2 & V3; V3 := F & V2; V3 := V2 & F;
1HPlease type a space to go on, or B to go back. 3223 312B310$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$The operators and, or, xor, and not, defined for BOOLEANs, are also defined for
one-dimensional arrays of BOOLEAN. They operate element by element on the
arrays. Thus, we can simulate sets in Ada. For example, if we write
then S1 or S2 is SET_OF_CHARS'('*' | '#' | '?' => TRUE, others => FALSE);,
and S1 and S2 is SET_OF_CHARS'('*' => TRUE, others => FALSE);.
The operators = and /= can compare two records or two arrays of the same type.
Records are equal if each of their corresponding fields are equal, arrays, if
each of their corresponding elements are equal. Arrays of different lengths
are always unequal. The four remaining relational operators can compare two
arrays of the same type. They're compared element by element until a
difference is found. For example, if we have
S : STRING(1 .. 6) := "to all";
T : STRING(1 .. 7) := "to Bill";
then S > T because 'a' > 'B' in Ada's definition of type CHARACTER.
1HPlease type a space to go on, or B to go back. 2713 313B311$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ OUTSIDE ASSIGNMENT 3 - EXERCISE IN RECORDS
Your third Outside Assignment is to write a function specified by
type MONTH_TYPE is (JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC);
subtype DAY_SUBTYPE is INTEGER range 1 .. 31;
type DATE is
record
DAY : DAY_SUBTYPE;
MONTH : MONTH_TYPE;
YEAR : INTEGER;
end record;
function TOMORROW(TODAY : in DATE) return DATE;
Given any date, TOMORROW should return the following date. Your function will
be tested only with legal dates. As with Outside Assignment 2, a test driver
is already written; it's in NEXTDATE.ADA. A listing is on page 12 of your
printed course notes, but you needn't understand the test driver. If your
function fails any test, you'll see the test case, the answer from your
function, and the right answer. Otherwise, you'll see "Congratulations, you
completed the assignment!"
1HPlease type a space to go on, or B to go back. 3029 314B312$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$The definitions of MONTH_TYPE, DAY_SUBTYPE, and DATE are in the test driver;
you shouldn't define them inside your function. A dummy solution is in
TOMORROW.DUM; it looks like this:
-- Dummy solution to Outside Assignment 3
separate (NEXTDATE)
function TOMORROW(TODAY : in DATE) return DATE is
begin
return TODAY;
end TOMORROW;
Again, you'll probably want to remove or change the comment line, and you
shouldn't change the lines highlighted above. You may, but don't have to,
include ANSWER : DATE; in the declarative region and make return ANSWER; the
last statement before end TOMORROW;.
Normally, years divisible by 4 are leap years. But if a year is divisible by
100, it must also be divisible by 400 to be a leap year. Thus, 2000 is a leap
year, but 1900 and 2100 are not. Note that TODAY.YEAR is divisible by 4 if and
only if TODAY.YEAR mod 4 = 0. You can assume that TODAY.YEAR will always be
between 1583 and 3999, because outside this range the calendar gets more
complicated.
1HPlease type a space to go on, or B to go back. 2512 315B313$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$The steps to follow for Outside Assignment 3 are very similar to those of
Outside Assignment 2. They're in your printed course notes on page 13:
1. Compile the test driver NEXTDATE.ADA. Also, make a copy of the dummy
solution by typing COPY TOMORROW.DUM TOMORROW.ADA. You need do this step
only once.
2. Edit TOMORROW.ADA to become your real solution. You can skip this step the
first time through, to see error messages from the test driver.
3. Compile TOMORROW.ADA. If there are compiler errors, go back to step 2.
4. Link with the name of the main program NEXTDATE. Then execute. If the
test driver prints error messages, go back to step 2.
5. When the message "Congratulations, you completed the assignment!" is
printed, you'll have a chance to compare your solution with ours.
1HPlease type a space to go on, or B to go back. 3119 316B314$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$There are many ways to solve this problem. In our solution we declared an
array of DAY_SUBTYPE with subscripts of type MONTH_TYPE. Some students use a
case construct on TODAY.MONTH; some use an if block with elsifs.
This assignment should be a simple exercise in records. Our solution fits on
one screen. If your solution starts to get long and difficult, you should
think the problem through again. Don't try to save the computer a few
microseconds; computers are supposed to save people time. Instead, minimize
the complexity of the program to save yourself programming effort.
Remember that an entire record can be assigned in one statement. Also, try to
calculate the number of days in the month and then test for end of month and
end of year in only one place. This is better than having several blocks of
code testing for end of month and end of year in three or four different places
in your program. One last hint: remember that MONTH_TYPE'SUCC(TODAY.MONTH)
will raise a CONSTRAINT_ERROR if TODAY.MONTH is DEC.
Please type X to exit ADA-TUTR temporarily, and try Outside Assignment 3. Work
at your own pace; there's no deadline. Good luck!
1HPlease type X to exit, a space to go on, or B to go back. 2645 317B315$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Congratulations on Completing Outside Assignment 3!
-- Our solution to Outside Assignment 3 (in TOMORROW.ANS):
separate (NEXTDATE)
function TOMORROW(TODAY : in DATE) return DATE is
LENGTH : array(MONTH_TYPE) of DAY_SUBTYPE :=
(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
ANSWER : DATE;
begin
if TODAY.YEAR mod 4 = 0 and
(TODAY.YEAR mod 100 /= 0 or TODAY.YEAR mod 400 = 0) then
LENGTH(FEB) := 29;
end if;
if TODAY.DAY /= LENGTH(TODAY.MONTH) then -- Not end of month.
1HPlease type a space to go on, or B to go back. 2760 318B316$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$In our solution, the first if statement checks for leap year. We made use of
the fact that the locally declared array LENGTH is re-initialized every time
the function is called. After this if statement, the number of days in the
month is in LENGTH(TODAY.MONTH), and it's easy to test for end of month and end
of year - in only one place in the program.
We could have written MONTH_TYPE'LAST instead of DEC, and MONTH_TYPE'FIRST
instead of JAN. We could even have used DAY_SUBTYPE'FIRST. But the author
reasoned that if our calendar ever changes, the entire program will probably
have to be rewritten anyway! The program seems a little easier to read when we
use the names DEC and JAN and the number 1 directly.
Your solution might be entirely different from ours. It the test driver said
"Congratulations, you completed the assignment!" you can consider your solution
correct. Let's go on to discuss recursion.
1HPlease type a space to go on, or B to go back. 2852 319B317$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ RECURSION
Ada procedures and functions may call themselves, directly or indirectly. This
process is called recursion. While recursion often uses a little extra memory
and execution time, for certain types of problems it pays large dividends in
program simplicity. That's a very worthwhile exchange!
For example, let's write a function to compute the factorial of a positive
integer. The factorial of an integer is the product of all the integers from
that number down to one. Thus, FACTORIAL(5) = 5 * 4 * 3 * 2 * 1 = 120.
Although we could easily write this function with a for loop, we'll use
recursion instead. Note that if N = 1, then FACTORIAL(N) = 1; otherwise,
FACTORIAL(N) = N * FACTORIAL(N - 1).
function FACTORIAL(N : in POSITIVE) return POSITIVE is
ANSWER : POSITIVE := 1;
begin
if N > 1 then
ANSWER := N * FACTORIAL(N - 1);
end if;
return ANSWER;
end FACTORIAL;
1HPlease type a space to go on, or B to go back. 3020 320B318$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ function FACTORIAL(N : in POSITIVE) return POSITIVE is
ANSWER : POSITIVE := 1;
begin
if N > 1 then
ANSWER := N * FACTORIAL(N - 1);
end if;
return ANSWER;
end FACTORIAL;
The highlighted line shows where this function calls itself. Recursive
subprograms always call themselves conditionally; otherwise the program would
run out of memory, no matter how large the machine. Here the recursive call is
inside an if block.
Note that ANSWER is initialized to 1. If the function is called with N = 1,
the if statement is FALSE, ANSWER isn't modified, and 1 is returned as the
value of the function. If the function is called with N = 2, the if statement
is TRUE, and the highlighted line is executed. The machine starts to compute
the expression as 2 * ..., and then FACTORIAL is called with N = 1. It's as if
the machine made another copy of the code for FACTORIAL and called that.
Actually there's only one copy of the code, but the machine maintains multiple
pointers to it.
1HPlease type a space to go on, or B to go back. 2866 321B319$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ function FACTORIAL(N : in POSITIVE) return POSITIVE is
ANSWER : POSITIVE := 1;
begin
if N > 1 then
ANSWER := N * FACTORIAL(N - 1);
end if;
return ANSWER;
end FACTORIAL;
When the recursive call is made, the system allocates additional memory for new
versions of any local variables like ANSWER. Also, the arguments like N are
separate for each recursive call.
Suppose the main program calls FACTORIAL with N = 2. Then, in computing the
expression, FACTORIAL calls itself with N = 1. In this second call, a new
local variable ANSWER is created, separate from the one in the first call.
Also, argument N is 1 in the second call, but 2 in the first call. In the
second call, the if statement is FALSE, and 1 is returned. In the first call,
the calculation of the expression is completed, using the value 1 returned by
the second call. Thus, ANSWER becomes 2 * 1 = 2, and the first call returns
the answer 2 to the main program.
1HPlease type a space to go on, or B to go back. 3012 322B320$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ function FACTORIAL(N : in POSITIVE) return POSITIVE is
ANSWER : POSITIVE := 1;
begin
if N > 1 then
ANSWER := N * FACTORIAL(N - 1);
end if;
return ANSWER;
end FACTORIAL;
If the main program calls FACTORIAL with N = 3, the function starts to compute
the expression as ANSWER := 3 * ..., and calls FACTORIAL with N = 2. In this
second call, the function starts to compute ANSWER := 2 * ..., and calls
FACTORIAL with N = 1. In this third call, the if statement is FALSE, and the
answer 1 is returned to the second call. The second call finishes the
computation of the expression ANSWER := 2 * 1, and returns the answer 2 to the
first call. Finally, the first call finishes computing the expression with
ANSWER := 3 * 2, and returns the answer 6 to the main program.
This simple function wouldn't have been complicated even without recursion. In
a moment we'll discuss the Tower of Hanoi problem. The recursive solution is
very simple, but a solution without recursion would be very complicated indeed.
1HPlease type a space to go on, or B to go back. 24502323032413243324B321$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$In this question, function A calls B, and B conditionally calls A. The
specification of function B is given early so that A can call it.
function B (I : in INTEGER) return INTEGER;
function A (I : in INTEGER) return INTEGER is
begin
return B(I - 1) + 1;
end A;
function B (I : in INTEGER) return INTEGER is
ANS : INTEGER := 0;
begin
if I > 0 then
ANS := A(I);
end if;
return ANS;
end B;
If the main program calls function A with an argument equal to 2, what value
will A return: 0, 1, 2, or 3? You may need pencil and paper to figure this
one out.
1HPlease press 0, 1, 2, or 3, or B to go back. 2628 325B322Q322$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ function B (I : in INTEGER) return INTEGER;
function A (I : in INTEGER) return INTEGER is
begin
return B(I - 1) + 1;
end A;
function B (I : in INTEGER) return INTEGER is
ANS : INTEGER := 0;
begin
if I > 0 then
ANS := A(I);
end if;
return ANS;
end B;
You're right! When the main program calls A(2), A starts to evaluate
B(1) + 1. The if statement in B is TRUE, so B simply calls A(1). The call
A(1) starts to evaluate B(0) + 1. The if statement in this call to B is FALSE,
so B(0) simply returns 0, and the call A(1) returns 0 + 1 = 1. The call B(1)
returns the same answer, so the call A(2) returns 1 + 1 = 2.
1HPlease type a space to go on, or B or Q to go back to the question. 2574 325B322Q322$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ function B (I : in INTEGER) return INTEGER;
function A (I : in INTEGER) return INTEGER is
begin
return B(I - 1) + 1;
end A;
function B (I : in INTEGER) return INTEGER is
ANS : INTEGER := 0;
begin
if I > 0 then
ANS := A(I);
end if;
return ANS;
end B;
No, when the main program calls A(2), A starts to evaluate B(1) + 1. The if
statement in B is TRUE, so B simply calls A(1). The call A(1) starts to
evaluate B(0) + 1. The if statement in this call to B is FALSE, so B(0) simply
returns 0, and the call A(1) returns 0 + 1 = 1. The call B(1) returns the same
answer, so the call A(2) returns 1 + 1 = 2.
1HPlease type a space to go on, or B or Q to go back to the question. 3026 326B322$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ THE TOWER OF HANOI PROBLEM
The "Tower of Hanoi" is a solitaire puzzle that was named when Hanoi was the
capital of a free country: French Indochina. There are three pegs labeled A,
B, and C; one of them has a tower of N doughnut-shaped disks of decreasing
The object is to move the entire tower from one peg to another, say, from A to
B. Only one disk may be moved at a time, and a larger disk may never be placed
on top of a smaller one. The shortest solution to the puzzle with N disks
requires 2**N - 1 moves.
1HPlease type a space to go on, or B to go back. 3160 327B325$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$For example, we can move five disks from A to B with the following 31 moves:
(Read them from left to right, not in columns.)
A to B, A to C, B to C, A to B, C to A, C to B, A to B, A to C, B to C,
B to A, C to A, B to C, A to B, A to C, B to C, A to B, C to A, C to B,
A to B, C to A, B to C, B to A, C to A, C to B, A to B, A to C, B to C,
Writing a program to print this series of moves would be very complicated
without recursion. Let's develop a recursive solution; we'll see that the
resulting Ada program is surprisingly simple!
1HPlease type a space to go on, or B to go back. 3217 328B326$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$When we developed a recursive solution for the FACTORIAL function:
if N = 1, FACTORIAL(N) = 1
otherwise, FACTORIAL(N) = N * FACTORIAL(N - 1)
we expressed FACTORIAL(N) in terms of FACTORIAL(N - 1), and gave the trivial
solution for N = 1. The Ada program was easily written from the above.
For the Tower of Hanoi problem, let's develop a solution for N disks in terms
of a solution for N - 1 disks. Suppose we want to move five disks from A to B,
using C as a spare peg. We first move four disks from A to C, then move one
disk from A to B, and finally move four disks from C to B. In general, to move
N disks from a source peg to a destination peg, we first move N - 1 disks from
the source to the spare, then move one disk from the source to the destination,
and finally move N - 1 disks from the spare to the destination.
To move the one disk from the source to the destination, our program will
simply print out the move. To move N - 1 disks, the program will call itself.
The solution for zero disks is trivial indeed: the program will do nothing!
The following program is extremely simple: three executable statements inside
an if block. Yet it can print out a very complicated series of moves!
1HPlease type a space to go on, or B to go back. 2820 329B327$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO; use TEXT_IO;
procedure HANOI(N : in NATURAL; FROM, TO, SPARE : in CHARACTER) is
begin
if N > 0 then
HANOI(N - 1, FROM, SPARE, TO);
PUT_LINE(FROM & " to " & TO);
HANOI(N - 1, SPARE, TO, FROM);
end if;
end HANOI;
To move five disks from A to B, using C as a spare, we would call
HANOI(5, 'A', 'B', 'C');.
Note that when HANOI is called with N = 0, it does nothing. When called with
N = 1, the if statement is true, and the three lines within the if block are
executed. But the first and third lines do nothing, because they call HANOI
with N = 0. The second line prints, for example, A to B.
When called with a larger value of N, the first line within the if block moves
N - 1 disks from the source to the spare peg. The second line prints the move
of one disk from the source to the destination, and the third line moves N - 1
disks from the spare peg to the destination.
1HPlease type a space to go on, or B to go back. 2656 330B328$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Most implementations of Ada won't allow HANOI to be a main program, because it
has arguments. A short main program to call HANOI is shown here. A slightly
longer program could get N and the names of the three pegs from the user.
with HANOI;
procedure DEMO is
begin
HANOI(5, 'A', 'B', 'C');
end DEMO;
In summary, our first example of recursion, FACTORIAL, was very simple.
However, that program would have been simple even without recursion. Our
second example, HANOI, also was very simple, but the program would have been
quite complicated without recursion.
Our fourth Outside Assignment will be to write a Fibonacci function FIB, using
recursion. As with FACTORIAL, this function would be easy to write even
without recursion. However, as an exercise we'll write it using recursion.
1HPlease type a space to go on, or B to go back. 2861 331B329$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ OUTSIDE ASSIGNMENT 4 - EXERCISE IN RECURSION
Your fourth Outside Assignment is to write, using recursion, a function
specified by
function FIB(N : in POSITIVE) return POSITIVE;
Fibonacci was a mathematician in the Middle Ages. The so-called Fibonacci
Series is a series of integers. Each number in the series is the sum of the
two previous numbers. The first two Fibonacci numbers are both 1.
Note that if N = 1 or N = 2, then FIB(N) = 1; otherwise,
FIB(N) = FIB(N - 1) + FIB(N - 2). Writing this function in Ada will be an easy
and short assignment. A test driver is provided in FIBTEST.ADA; a listing is
on page 14 of your printed course notes. As before, if all tests are passed,
it prints "Congratulations, you completed the assignment!" If there's an
error, it prints the test case, the answer from your function, and the right
answer.
1HPlease type a space to go on, or B to go back. 2013 332B330$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$A dummy solution is in FIB.DUM. As before, you shouldn't change the lines
highlighted here:
-- Dummy solution to Outside Assignment 4
separate (FIBTEST)
function FIB(N : in POSITIVE) return POSITIVE is
begin
return 4;
end FIB;
The steps to follow for Outside Assignment 4 are similar to those of the last
two Outside Assignments. They're in your printed course notes on page 15:
1HPlease type a space to go on, or B to go back. 2454 333B331$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$1. Compile the test driver FIBTEST.ADA. Also, make a copy of the dummy
solution by typing COPY FIB.DUM FIB.ADA. You need do this step only once.
2. Edit FIB.ADA to become your real solution. You can skip this step the
first time through, to see error messages from the test driver.
3. Compile FIB.ADA. If there are compiler errors, go back to step 2.
4. Link with the name of the main program FIBTEST. Then execute. If the test
driver prints error messages, go back to step 2.
5. When the message "Congratulations, you completed the assignment!" is
printed, you'll have a chance to compare your solution with ours.
Please type X to exit ADA-TUTR temporarily, and try Outside Assignment 4. Work
at your own pace; there's no deadline. Good luck!
1HPlease type X to exit, a space to go on, or B to go back. 2811 334B332$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Congratulations on Completing Outside Assignment 4!
-- Our solution to Outside Assignment 4 (in FIB.ANS):
separate (FIBTEST)
function FIB(N : in POSITIVE) return POSITIVE is
ANSWER : POSITIVE := 1;
begin
if N > 2 then
ANSWER := FIB(N - 1) + FIB(N - 2);
end if;
return ANSWER;
end FIB;
Your solution is probably quite similar to ours. Perhaps you used else and
didn't initialize ANSWER in the declarative region. Perhaps you had some other
minor variation. Our solution is probably no "better" than yours, if the test
driver said, "Congratulations, you completed the assignment!"
Was this assignment too easy? We promise that Outside Assignment 5 will be
much more challenging! But first we need to discuss procedures and functions
in more detail, as well as packages and information hiding, access types, and
exceptions.
1HPlease type a space to go on, or B to go back. 2620 335B333$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ PROCEDURES AND FUNCTIONS
When we compiled procedures HELLO and ADD into the library, we made it possible
for other units to with them and call them. (We don't use procedures and
functions, because dot notation doesn't apply to them.) We can also compile
just a specification, supplying the body later. For example, we could compile
function UPPER_CASE(S : in STRING) return STRING;
When we later write the body, it must agree with that specification:
function UPPER_CASE(S : in STRING) return STRING is
1HPlease type a space to go on, or B to go back. 2613 336B334$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Functions and procedures may also be declared locally, in which case they must
follow any simple declarations like I : INTEGER; or S : STRING(1 .. 5);.
with TEXT_IO; use TEXT_IO;
procedure GREETING is
S : STRING(1 .. 5) := "Hello";
function UPPER_CASE(S : in STRING) return STRING is
As we've seen, we can declare local functions and procedures to be separate.
These subprograms can in turn declare separate subprograms, to any depth:
1HPlease type a space to go on, or B to go back. 2316 337B335$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ procedure MAIN is
function A return FLOAT is separate;
begin
...
end MAIN;
separate (MAIN)
function A return FLOAT is
procedure B is separate;
begin
...
end A;
separate (MAIN.A)
procedure B is
procedure C(I : in INTEGER) is separate;
begin
...
end B;
separate (MAIN.A.B)
procedure C(I : in INTEGER) is ... (etc.)
1HPlease type a space to go on, or B to go back. 2764 338B336$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$However, is separate may be used only at the outermost level. The example
below is legal as it stands, but we may not make procedure B separate unless we
make function A separate, thus bringing function A to the outermost level:
procedure MAIN is
F : FLOAT;
function A return FLOAT is
ANSWER : FLOAT;
procedure B is
begin
...
end B;
begin
...
return ANSWER;
end A;
begin
...
end MAIN;
A program that begins separate (...) must be compiled after the program that
says is separate, because a separate subprogram depends on its "parent."
1HPlease type a space to go on, or B to go back. 2844 339B337$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$A procedure or function specification gives the name, mode, and type of each
argument. A function specification also gives the type of the result. The
mode can be in, out, or in out. If the mode is omitted, it's assumed to be in.
Thus, these two lines have the same effect:
procedure HANOI(N : in NATURAL; FROM, TO, SPARE : in CHARACTER);
procedure HANOI(N : NATURAL; FROM, TO, SPARE : CHARACTER);
The arguments of a function must always be of mode in, never out or in out.
Note that when several arguments have the same mode and type, they can be
placed in one list, separated by commas. The lists themselves are separated by
semicolons. This specification has the same effect as the two above:
procedure HANOI(N : NATURAL; FROM: CHARACTER; TO: CHARACTER; SPARE: CHARACTER);
In any event, the arguments in a call to a procedure or function are always
separated by commas:
HANOI(5, 'A', 'B', 'C');
1HPlease type a space to go on, or B to go back. 16544340134123423343B338$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. procedure P(A; B; C : in INTEGER);
2. procedure P(A, B, C : INTEGER; D, E : in out FLOAT) return CHARACTER;
3. function F(A, B, C : INTEGER; D, E : in out FLOAT) return CHARACTER;
4. procedure P(A, B, C : INTEGER; D, E : in out FLOAT);
Which one of the above is legal?
1HPlease press 1, 2, 3, or 4, or B to go back. 2318 344B339Q339$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. procedure P(A; B; C : in INTEGER);
2. procedure P(A, B, C : INTEGER; D, E : in out FLOAT) return CHARACTER;
3. function F(A, B, C : INTEGER; D, E : in out FLOAT) return CHARACTER;
4. procedure P(A, B, C : INTEGER; D, E : in out FLOAT);
You're right! In a subprogram specification, the items of a list are separated
by commas, and the lists are separated by semicolons. In a list, the mode may
be omitted; it's assumed to be in.
In number 1, the items should be separated by commas. Number 2 has a return
clause in a procedure specification, and number 3 has a mode other than in in a
function specification.
1HPlease type a space to go on, or B or Q to go back to the question. 2074 344B339Q339$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. procedure P(A; B; C : in INTEGER);
2. procedure P(A, B, C : INTEGER; D, E : in out FLOAT) return CHARACTER;
3. function F(A, B, C : INTEGER; D, E : in out FLOAT) return CHARACTER;
4. procedure P(A, B, C : INTEGER; D, E : in out FLOAT);
No, in number 1 the items should be separated by commas, not semicolons. In a
subprogram specification, the items in a list are separated by commas, and the
lists are separated by semicolons. Here a "list" means a collection of
arguments sharing the same mode and type (in INTEGER).
1HPlease type a space to go on, or B or Q to go back to the question. 1836 344B339Q339$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. procedure P(A; B; C : in INTEGER);
2. procedure P(A, B, C : INTEGER; D, E : in out FLOAT) return CHARACTER;
3. function F(A, B, C : INTEGER; D, E : in out FLOAT) return CHARACTER;
4. procedure P(A, B, C : INTEGER; D, E : in out FLOAT);
No, number 2 is illegal because a return clause applies only to a function
specification, not a procedure specification.
1HPlease type a space to go on, or B or Q to go back to the question. 1757 344B339Q339$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. procedure P(A; B; C : in INTEGER);
2. procedure P(A, B, C : INTEGER; D, E : in out FLOAT) return CHARACTER;
3. function F(A, B, C : INTEGER; D, E : in out FLOAT) return CHARACTER;
4. procedure P(A, B, C : INTEGER; D, E : in out FLOAT);
No, number 3 is illegal because all arguments of a function must have mode in.
1HPlease type a space to go on, or B or Q to go back to the question. 3111 345B339$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ DEFAULT PARAMETERS
The in parameters of a subprogram specification may be given default values.
Suppose, for example, that the package INTEGER_IO is instantiated for the type
INTEGER. Here's a simplified version of the specification for the procedure
PUT (the actual specification can be found in section 14.3.10 of the LRM):
procedure PUT(ITEM : in INTEGER;
WIDTH : in INTEGER := 6;
BASE : in INTEGER := 10);
This means that, in calls to PUT, the WIDTH and BASE arguments are optional.
If WIDTH is omitted, it's assumed to be 6, and if BASE is omitted, it's assumed
to be 10. If either of these arguments is given in the call, the default value
is overridden. (The default value for WIDTH is shown here as 6. Actually, it
depends on the implementation of Ada. Of course, the default value for BASE is
always 10.)
Default parameters let us make our Ada subprograms both flexible and easy to
use. In other languages, we'd often have to choose between these two
qualities. For example, suppose J is an integer. Here are some calls to PUT:
1HPlease type a space to go on, or B to go back. 3114 346B344$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ procedure PUT(ITEM : in INTEGER;
WIDTH : in INTEGER := 11;
BASE : in INTEGER := 10);
PUT(J);
PUT(J, WIDTH => 4);
PUT(J, BASE => 16);
PUT(J, BASE => 16, WIDTH => 4);
The first argument in each call could have been given as ITEM => J, but
everyone remembers that the first argument of PUT is the item, so named
notation seems unnecessary for this argument. However, WIDTH and BASE are used
less frequently. We used named notation for these arguments so the reader of
our code wouldn't have to remember which argument comes second and which comes
third. Note that if we omit the second argument and specify the third, we must
use named notation for the third argument; we're not allowed to write
PUT(J, ,16); as in some languages.
If we were writing PUT in another language, we'd have to choose either making
the user specify the width and the base in every call, giving flexibility, or
writing PUT with only one argument, giving ease of use. In Ada, default
parameters give us both flexibility and ease of use!
1HPlease type a space to go on, or B to go back. 2616 347B345$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$TEXT_IO.NEW_LINE has one parameter, SPACING, defaulted to 1. Thus, we can call
NEW_LINE; to get one CR-LF, or, for example, NEW_LINE(3); to get three.
Default values may also be given in record definitions. If we write
type MONTH_TYPE is (JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC);
subtype DAY_SUBTYPE is INTEGER range 1 .. 31;
type DATE is
record
DAY : DAY_SUBTYPE;
MONTH : MONTH_TYPE;
YEAR : INTEGER := 1776;
end record;
USA : DATE;
then USA.YEAR is set to 1776 when the line declaring USA is elaborated. Every
time an object of type DATE is declared, its YEAR field is set to 1776.
However, there's a difference between default values in records and default
parameters in subprograms. We can't write USA := (4, JUL); all three fields of
the record must be specified.
1HPlease type a space to go on, or B to go back. 1744234813493350B346$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ procedure PUT(ITEM : in INTEGER;
WIDTH : in INTEGER := 11;
BASE : in INTEGER := 10);
Which one of these is legal?
1. PUT(ITEM => 23, 5, 10);
2. PUT(23, 5);
3. PUT(23; 5; 10);
1HPlease press 1, 2, or 3, or B to go back. 2063 351B347Q347$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ procedure PUT(ITEM : in INTEGER;
WIDTH : in INTEGER := 11;
BASE : in INTEGER := 10);
1. PUT(ITEM => 23, 5, 10);
2. PUT(23, 5);
3. PUT(23; 5; 10);
You're right! The third argument may be omitted because it has a default
value. In number 1, positional notation follows named, which isn't allowed,
and in number 3, the separators should be commas.
1HPlease type a space to go on, or B or Q to go back to the question. 1848 351B347Q347$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ procedure PUT(ITEM : in INTEGER;
WIDTH : in INTEGER := 11;
BASE : in INTEGER := 10);
1. PUT(ITEM => 23, 5, 10);
2. PUT(23, 5);
3. PUT(23; 5; 10);
No, number 1 is illegal because positional notation may not follow named.
1HPlease type a space to go on, or B or Q to go back to the question. 1918 351B347Q347$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ procedure PUT(ITEM : in INTEGER;
WIDTH : in INTEGER := 11;
BASE : in INTEGER := 10);
1. PUT(ITEM => 23, 5, 10);
2. PUT(23, 5);
3. PUT(23; 5; 10);
No, number 3 is illegal because in a call, the arguments are always separated
with commas, not semicolons.
1HPlease type a space to go on, or B or Q to go back to the question. 2856 352B347$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ PACKAGES
A package lets us group related declarations, procedures, and functions. A
program can with the package and gain access to all of these. However, as
we'll see, packages have many other advantages. Usually library units are
packages rather than individual procedures and functions.
By way of example, consider a very simple procedure and a very simple function:
procedure DOUBLE(NUMBER : in INTEGER; ANSWER : out INTEGER);
function TWICE(NUMBER : in INTEGER) return INTEGER;
We could compile these individually, or we could put them in a package and
compile that instead. Here's the package specification:
package SIMPLE_MATH is
procedure DOUBLE(NUMBER : in INTEGER; ANSWER : out INTEGER);
function TWICE(NUMBER : in INTEGER) return INTEGER;
end SIMPLE_MATH;
The package body must contain the bodies of all the procedures and functions
declared in the package specification:
1HPlease type a space to go on, or B to go back. 2522 353B351$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ package body SIMPLE_MATH is
procedure DOUBLE(NUMBER : in INTEGER; ANSWER : out INTEGER) is
begin
ANSWER := NUMBER * 2;
end DOUBLE;
function TWICE(NUMBER : in INTEGER) return INTEGER is
begin
return NUMBER * 2;
end TWICE;
end SIMPLE_MATH;
The package body could optionally declare either or both subprograms to be
separate:
package body SIMPLE_MATH is
procedure DOUBLE(NUMBER : in INTEGER; ANSWER : out INTEGER) is separate;
function TWICE(NUMBER : in INTEGER) return INTEGER is separate;
end SIMPLE_MATH;
Here's an example of a calling program that withs the package and makes use of
the two subprograms declared in the package specification:
1HPlease type a space to go on, or B to go back. 2936 354B352$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with SIMPLE_MATH; use SIMPLE_MATH;
procedure PACKAGE_DEMO is
I, J : INTEGER;
begin
I := 10;
DOUBLE(NUMBER => I, ANSWER => J); -- This line sets J to 20.
J := TWICE(I); -- This line also sets J to 20.
end PACKAGE_DEMO;
The package specification must be compiled first, but either the calling
program or the package body may be compiled second. The calling program
depends only on the package specification, not the package body. Similarly,
the package body depends on the package specification. If the package body
declares any subprograms to be separate, these must be compiled after the
package body, because any separate subprogram depends on its "parent."
A package specification that declares no subprograms (only objects, types,
etc.) doesn't need a package body.
The main program can never be inside a package. It must be a procedure
compiled directly into the library, such as HELLO or ADD. In most
implementations of Ada, that procedure can't have any arguments.
1HPlease type a space to go on, or B to go back. 2528 355B353$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$A package body may optionally have initialization code, introduced by the word
begin. This code is executed only once, the first time another program
elaborates the package by withing it. For example, this package uses
initialization code to initialize the array B:
package P is
function F;
end P;
package body P is
A : array(1 .. 10) of FLOAT := (others => 0.0);
B : array(1 .. 10, 1 .. 10) of INTEGER;
function F is
...
end F;
begin
for I in 1 .. 10 loop
for J in 1 .. 10 loop
B(I, J) := I*J*J;
end loop;
end loop;
end P;
1HPlease type a space to go on, or B to go back. 3071 356B354$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Declarations made inside a package specification are said to be exported and
are available to programs outside the package that with the package. However,
declarations made inside the package body are available only inside the
package. The package body doesn't even have to be written before outside
programs that with the package. In this example, programs outside and inside
the package may use type ANSWER, array A, and procedure R, but only procedures
P, Q and R may use type QUESTION and array B. Procedure P may be called only
by Q and R (and itself), and procedure Q is available only to R (and itself).
package X is
type ANSWER is (YES, NO, MAYBE);
A : array(1 .. 10) of FLOAT;
procedure R;
end X;
package body X is
type QUESTION is (WHY, WHO, HOW);
B : array(1 .. 10) of INTEGER;
procedure P is separate;
procedure Q is separate;
procedure R is separate;
end X;
1HPlease type a space to go on, or B to go back. 2157T357F358B355$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ package X is
type ANSWER is (YES, NO, MAYBE);
A : array(1 .. 10) of FLOAT;
procedure R;
end X;
package body X is
type QUESTION is (WHY, WHO, HOW);
B : array(1 .. 10) of INTEGER;
procedure P is separate;
procedure Q is separate;
procedure R is separate;
end X;
True or False? In this example, procedure P may call R.
1HPlease press T for true or F for false, or B to go back. 2344 359B356Q356$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ package X is
type ANSWER is (YES, NO, MAYBE);
A : array(1 .. 10) of FLOAT;
procedure R;
end X;
package body X is
type QUESTION is (WHY, WHO, HOW);
B : array(1 .. 10) of INTEGER;
procedure P is separate;
procedure Q is separate;
procedure R is separate;
end X;
You're right! R is declared in the package specification, so any program
inside X (and any program outside X that withs the package) may call R.
1HPlease type a space to go on, or B or Q to go back to the question. 2329 359B356Q356$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ package X is
type ANSWER is (YES, NO, MAYBE);
A : array(1 .. 10) of FLOAT;
procedure R;
end X;
package body X is
type QUESTION is (WHY, WHO, HOW);
B : array(1 .. 10) of INTEGER;
procedure P is separate;
procedure Q is separate;
procedure R is separate;
end X;
True. R is declared in the package specification, so any program inside X (and
any program outside X that withs the package) may call R.
1HPlease type a space to go on, or B or Q to go back to the question. 2944 360B356$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ FUNCTIONS WITH INFIX NOTATION
+ - * / ** & = < > <= >=
and or xor abs not mod rem
Ada allows us to overload any of the above operators by enclosing the operator
in quotes following the word function. For example, having defined type DATE,
we may want to define what it means for one date to be "less than" another. We
could write function "<"(LEFT, RIGHT : in DATE) return BOOLEAN;. Then, our
program could declare D1, D2 : DATE; and test if D1 < D2 then ... This test
would call our function "<", because < is used between two objects of type
DATE. Similarly, we can overload "*" to give the dot product of two vectors:
type VECTOR is array(INTEGER range <>) of FLOAT;
V1, V2 : VECTOR(1 .. 100);
X : FLOAT;
function "*"(LEFT, RIGHT : in VECTOR) return FLOAT is
...
end "*";
...
X := V1 * V2; -- This calls our function "*".
1HPlease type a space to go on, or B to go back. 3045 361B359$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + - * / ** & = < > <= >=
and or xor abs not mod rem
There are some restrictions when using infix notation. First, all of the
operators above (except abs, not, +, and -) must be used between two arguments,
and thus the function specification must have exactly two arguments.
Traditionally, the two arguments are called LEFT and RIGHT. The operators abs
and not must have one argument on the right, and + and - may have one or two
arguments. This restriction comes from the way the compiler handles operators.
For example, the compiler can handle X := - X;, but it's not designed to handle
X := * X;.
Second, the function "=" must return type BOOLEAN, its two arguments must be of
the same type, and that type must be a limited private type, to be discussed
later. For all the types discussed so far, function "=" is already defined by
Ada. Two records are equal if each of their corresponding fields is equal, and
two arrays are equal if they have the same length and each of their
corresponding elements is equal.
1HPlease type a space to go on, or B to go back. 2454 362B360$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + - * / ** & = < > <= >=
and or xor abs not mod rem
Note that we can't redefine function "/=". However, if we redefine "=" for
some limited private type, we can use /=. Ada will call our function "=" and
negate the result. For example,
type TEXT is limited private; -- to be discussed later
T1, T2 : TEXT;
function "="(LEFT, RIGHT : in TEXT) return BOOLEAN is
...
end "=";
...
if T1 /= T2 then -- Calls our "=" and reverses the result.
...
end if;
Also, we can't redefine in, not in, and then, or else, or :=. Technically,
these aren't operators.
1HPlease type a space to go on, or B to go back. 3028 363B361$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Finally, functions using infix notation can't be compiled directly into the
library. They must either be declared locally inside a procedure or function,
or placed inside a package. This is done because many implementations of Ada
create files with names based on the function, procedure, or package being
compiled. Since many of the operators are punctuation marks, they would create
file names that are incompatible with most systems.
For the same reason, two functions or procedures with the same name can't be
compiled directly into the library; on many systems that would attempt to give
the same name to several files. For example, the package specification below
is legal. However, the package body can't declare both DISPLAYs separate, and
it can't declare either "*" separate.
package P is
type COMPLEX is ...
type VECTOR is ...
procedure DISPLAY(C : in COMPLEX);
procedure DISPLAY(V : in VECTOR);
function "*"(LEFT, RIGHT : in COMPLEX) return COMPLEX;
function "*"(LEFT, RIGHT : in VECTOR) return FLOAT;
end P;
1HPlease type a space to go on, or B to go back. 23532364036513653366436653666366B362$$$$$$$$$$$$$$$$$$$$$$$$$$$$ package K is
type COMPLEX is
record
RE, IM : FLOAT;
end record;
type VECTOR is array(INTEGER range <>) of FLOAT;
function "+"(LEFT, RIGHT : in COMPLEX) return COMPLEX;
function "*"(LEFT, RIGHT : in COMPLEX) return COMPLEX;
function "*"(LEFT, RIGHT : in VECTOR) return FLOAT;
function CONJUGATE(C : in COMPLEX) return COMPLEX;
procedure DISPLAY(C : in COMPLEX);
procedure DISPLAY(V : in VECTOR);
end K;
The above is a package specification. In the package body, how many
subprograms could be made separate: 0, 1, 2, 3, 4, 5, or 6?
1HPlease press 0, 1, 2, 3, 4, 5, or 6, or B to go back. 2639 367B363Q363$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ package body K is
function "+"(LEFT, RIGHT : in COMPLEX) return COMPLEX is ...
function "*"(LEFT, RIGHT : in COMPLEX) return COMPLEX is ...
function "*"(LEFT, RIGHT : in VECTOR) return FLOAT is ...
function CONJUGATE(C : in COMPLEX) return COMPLEX is separate;
procedure DISPLAY(C : in COMPLEX) is ...
procedure DISPLAY(V : in VECTOR) is separate;
end K;
separate (K)
function CONJUGATE(C : in COMPLEX) return COMPLEX is
...
end CONJUGATE;
separate (K)
procedure DISPLAY(V : in VECTOR) is
...
end DISPLAY;
You're right! Only function CONJUGATE and at most one DISPLAY can be made
separate. Functions using infix notation can't be made separate, and the name
DISPLAY is overloaded.
1HPlease type a space to go on, or B or Q to go back to the question. 2466 367B363Q363$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ package K is
type COMPLEX is
record
RE, IM : FLOAT;
end record;
type VECTOR is array(INTEGER range <>) of FLOAT;
function "+"(LEFT, RIGHT : in COMPLEX) return COMPLEX;
function "*"(LEFT, RIGHT : in COMPLEX) return COMPLEX;
function "*"(LEFT, RIGHT : in VECTOR) return FLOAT;
function CONJUGATE(C : in COMPLEX) return COMPLEX;
procedure DISPLAY(C : in COMPLEX);
procedure DISPLAY(V : in VECTOR);
end K;
No, the function CONJUGATE can be made separate, because its name isn't
overloaded and it doesn't use infix notation. Also, one, but not both, DISPLAY
procedures can be made separate.
1HPlease type a space to go on, or B or Q to go back to the question. 2446 367B363Q363$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ package K is
type COMPLEX is
record
RE, IM : FLOAT;
end record;
type VECTOR is array(INTEGER range <>) of FLOAT;
function "+"(LEFT, RIGHT : in COMPLEX) return COMPLEX;
function "*"(LEFT, RIGHT : in COMPLEX) return COMPLEX;
function "*"(LEFT, RIGHT : in VECTOR) return FLOAT;
function CONJUGATE(C : in COMPLEX) return COMPLEX;
procedure DISPLAY(C : in COMPLEX);
procedure DISPLAY(V : in VECTOR);
end K;
No, only function CONJUGATE and at most one DISPLAY can be made separate.
Functions using infix notation can't be made separate, and the name DISPLAY is
overloaded.
1HPlease type a space to go on, or B or Q to go back to the question. 2349 368B363$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ INFORMATION HIDING: PRIVATE TYPES
Information hiding has nothing to do with secrecy; it means containing certain
programming details to a package so that the parts of the program outside the
package can't depend on them. Thus, when these details change, other parts of
the program aren't affected.
Let's write the specification (only) for a graphics CRT controller package
without information hiding. Then we'll improve on it by using a private type.
The package will let us create arrays in memory representing screens. We can
create as many of these "virtual screens" as we like, and draw dots, lines, and
boxes on them in memory. We can also display any virtual screen. Here's the
package specification:
1HPlease type a space to go on, or B to go back. 2948 369B367$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$package CRT_CONTROLLER is
type COLOR_TYPE is (WHITE, BLACK, RED, YELLOW, GREEN, BLUE);
type SCREEN_TYPE is array(0 .. 299, 0 .. 399) of COLOR_TYPE;
procedure DISPLAY (SCREEN : in SCREEN_TYPE);
procedure CLEAR (SCREEN : in out SCREEN_TYPE; TO: in COLOR_TYPE := BLACK);
type POINT is
record
ROW : INTEGER range 0 .. 299;
COLUMN : INTEGER range 0 .. 399;
end record;
procedure DOT (SCREEN : in out SCREEN_TYPE; PLACE : in POINT;
COLOR : in COLOR_TYPE);
procedure LINE (SCREEN : in out SCREEN_TYPE; FROM, TO : in POINT;
COLOR : in COLOR_TYPE);
procedure BOX (SCREEN : in out SCREEN_TYPE; CORNER1, CORNER2 : in POINT;
COLOR : in COLOR_TYPE);
end CRT_CONTROLLER;
Note that this package assumes that the resolution of the CRT is 300 vertically
by 400 horizontally. A screen is represented in memory by a two-dimensional
array, each element of which contains one of six possible colors. Procedures
to draw other figures (circles, etc.) could have been added to the package.
1HPlease type a space to go on, or B to go back. 2817 370B368$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$That package specification makes the calling program very clear! For example,
with CRT_CONTROLLER; use CRT_CONTROLLER;
procedure CRT_DEMO is
S1, S2 : SCREEN_TYPE;
begin
CLEAR(S1);
CLEAR(S2, TO => WHITE);
LINE(S1, FROM => (150, 100), TO => (150, 300), COLOR => GREEN);
The first executable line clears memory for screen S1 to all black, because of
the default parameter in the specification of CLEAR. The next line clears S2
to all white. The next three lines draw a line and a dot on S1 and display S1.
The program then draws a box and a dot on S2 and displays S2. Named notation
could optionally have been used for the first argument of each call, and for
the objects of type POINT, e.g., (ROW => 150, COLUMN => 100).
1HPlease type a space to go on, or B to go back. 1854137123723372B369$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$At a sacrifice in clarity (not recommended!), the calling program CRT_DEMO
could draw a dot in the center of screen S1 without calling DOT. Which of the
following statements would accomplish the same thing as
DOT(S1, PLACE => (150, 200), COLOR => RED);
1. S1(150, 200) := RED;
2. S1(ROW => 150, COLUMN => 200) := RED;
3. Either of the above would work.
1HPlease press 1, 2, or 3, or B to go back. 2013 373B370Q370$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ DOT(S1, PLACE => (150, 200), COLOR => RED);
1. S1(150, 200) := RED;
2. S1(ROW => 150, COLUMN => 200) := RED;
3. Either of the above would work.
You're right! At a sacrifice in clarity, the appropriate element of S1 can be
set directly to RED to draw a dot in the center of the screen. Number 2 is
wrong because the names ROW and COLUMN apply to type POINT, not to subscripts
of the array.
1HPlease type a space to go on, or B or Q to go back to the question. 1758 373B370Q370$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ DOT(S1, PLACE => (150, 200), COLOR => RED);
1. S1(150, 200) := RED;
2. S1(ROW => 150, COLUMN => 200) := RED;
3. Either of the above would work.
No, the names ROW and COLUMN apply to type POINT, not to subscripts of the
array. For this reason number 2 won't compile.
1HPlease type a space to go on, or B or Q to go back to the question. 3138 374B370$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Although the calling program is very clear, there's one disadvantage that can
be eliminated by using a private type: if the resolution of the screen is
changed, the calling program is affected. For example, the two calls to DOT
are each intended to draw a dot in the center of the screen. These calls will
have to be changed when the resolution changes, or the dot will no longer be in
the center. Also, nothing prevents CRT_DEMO from saying S1(150, 200) := RED;
directly, instead of calling DOT. This is a disadvantage, because accessing
the elements of the array directly makes CRT_DEMO very susceptible to changes
in the package.
It would be better to force CRT_DEMO to call our procedure DOT, and make it
impossible for CRT_DEMO to use the fact the a screen is represented by a
two-dimensional array. Then the representation of a screen can change without
affecting the correctness of CRT_DEMO.
We'll improve our package specification and calling program to make SCREEN_TYPE
a private type, so that its details can be used only inside the package. Since
the screen resolution won't be available outside the package, we'll normalize
ROW and COLUMN, making them floating point numbers in the range 0.0 .. 1.0.
1HPlease type a space to go on, or B to go back. 2632 375B373$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Here's our improved package specification:
package CRT_CONTROLLER is
type COLOR_TYPE is (WHITE, BLACK, RED, YELLOW, GREEN, BLUE);
type SCREEN_TYPE is private;
procedure DISPLAY (SCREEN : in SCREEN_TYPE);
procedure CLEAR (SCREEN : in out SCREEN_TYPE; TO: in COLOR_TYPE := BLACK);
type POINT is
record
ROW : FLOAT range 0.0 .. 1.0;
COLUMN : FLOAT range 0.0 .. 1.0;
end record;
procedure DOT (SCREEN : in out SCREEN_TYPE; PLACE : in POINT;
COLOR : in COLOR_TYPE);
procedure LINE (SCREEN : in out SCREEN_TYPE; FROM, TO : in POINT;
COLOR : in COLOR_TYPE);
procedure BOX (SCREEN : in out SCREEN_TYPE; CORNER1, CORNER2 : in POINT;
COLOR : in COLOR_TYPE);
private
type SCREEN_TYPE is array(0 .. 299, 0 .. 399) of COLOR_TYPE;
end CRT_CONTROLLER;
1HPlease type a space to go on, or B to go back. 2427 376B374$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ package CRT_CONTROLLER is
...
type SCREEN_TYPE is private;
procedure DISPLAY (SCREEN : in SCREEN_TYPE);
procedure CLEAR (SCREEN : in out SCREEN_TYPE; ... );
...
private
type SCREEN_TYPE is array(0 .. 299, 0 .. 399) of COLOR_TYPE;
end CRT_CONTROLLER;
Outside the package, there are only four things a calling program may do with
objects of a private type like SCREEN_TYPE:
1. Create them: S1, S2 : SCREEN_TYPE;
2. Assign them: S1 := S2;
3. Test them for equality and inequality: if S1 = S2 ... if S1 /= S2 ...
4. Use any procedures, functions, and infix operators provided by the package:
CLEAR(S1); DISPLAY(S2); etc.
1HPlease type a space to go on, or B to go back. 2833 377B375$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ With objects of a private type, outside the package we may only:
Note that the calling program, outside the package, can no longer say
S1(150, 200) := RED;, because that's not one of the four things listed above.
The calling program is forced to call DOT. The information that SCREEN_TYPE is
an array can be used by code only inside the package. Of course, the compiler
uses this information when compiling code outside or inside the package.
That's why the definition of SCREEN_TYPE must be in the package specification,
not the body. But the programmer isn't allowed to use this information outside
the package.
Inside the package there are no restrictions. To write the bodies of the
subprograms, we'll have to use the structure of SCREEN_TYPE and write
statements similar to S1(150, 200) := RED;.
Here's our calling program, revised to agree with the improved package
specification:
1HPlease type a space to go on, or B to go back. 3043 378B376$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with CRT_CONTROLLER; use CRT_CONTROLLER;
procedure CRT_DEMO is
S1, S2 : SCREEN_TYPE;
begin
CLEAR(S1);
CLEAR(S2, TO => WHITE);
LINE(S1, FROM => (0.5, 0.25), TO => (0.5, 0.75), COLOR => GREEN);
Now, if a change is made only to the private part of the package specification,
CRT_DEMO won't have to be revised. It will have to be recompiled, because the
Ada compiler needs the information in the private part of the package
specification. (The linker won't link with obsolete units, but will tell us
what me must recompile.) Also, the package body may have to be rewritten. But
if CRT_DEMO was correct before, it will remain correct without any revision!
The effects of the change are confined to the package. Containing the effects
of changes by means of information hiding is one of Ada's greatest features.
1HPlease type a space to go on, or B to go back. 1438F379T380B377$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$True or False? If the private part of a package specification is changed, the
calling program must be recompiled before the package body is recompiled.
1HPlease press T for true or F for false, or B to go back. 1544 381B378Q378$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$You're right! The package body and the calling program both depend on the
package specification, but not on each other. They can be compiled in either
order after the package specification is compiled.
1HPlease type a space to go on, or B or Q to go back to the question. 1537 381B378Q378$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$False. The package body and the calling program don't depend on each other,
but only on the package specification. Thus they can be compiled in either
order after the package specification is compiled.
1HPlease type a space to go on, or B or Q to go back to the question. 2439 382B378$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$If a package exports a constant of a private type, the constant is declared in
the "public" part of the package specification, but it can't be assigned a
value until we get to the private part. This is called a deferred constant.
For example,
package CRT_CONTROLLER is
type COLOR_TYPE is (WHITE, BLACK, RED, YELLOW, GREEN, BLUE);
type SCREEN_TYPE is private;
FLAG_OF_ITALY : constant SCREEN_TYPE;
procedure DISPLAY (SCREEN : in SCREEN_TYPE);
...
private
type SCREEN_TYPE is array(0 .. 299, 0 .. 399) of COLOR_TYPE;
CRT_DEMO could then say S1 := FLAG_OF_ITALY; or DISPLAY(FLAG_OF_ITALY);.
1HPlease type a space to go on, or B to go back. 2950 383B381$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ TYPE TEXT AND LIMITED PRIVATE TYPES
Earlier we remarked that Ada strings are of fixed length. Section 7.6 of the
LRM suggests that we create a type TEXT to get around this restriction.
Instead of declaring objects to be STRINGs, we'll declare them to be of type
TEXT, which will simulate variable-length strings. Then we'll see how this
creates a need for limited private types.
The package specification in section 7.6 of the LRM makes use of discriminated
records. Since we haven't yet covered that topic, we'll give a simplified
presentation here. Let's declare
type TEXT is
record
LEN : INTEGER range 0 .. 80 := 0;
VAL : STRING(1 .. 80);
end record;
Any appropriate maximum length may be used in place of 80. It isn't necessary
to initialize the VAL field to all blanks, because the LEN field keeps track of
how much of the VAL field is significant. The LEN field is given a default
value of 0, so that objects of type TEXT will be initialized to empty.
1HPlease type a space to go on, or B to go back. 2515 384B382$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type TEXT is
record
LEN : INTEGER range 0 .. 80 := 0;
VAL : STRING(1 .. 80);
end record;
For example, we could declare T1 : TEXT;. We could then set T1.LEN := 5; and
T1.VAL(1 .. 5) := "Hello";. The fact that the last 75 characters of T1.VAL
might contain garbage is of no consequence, because T1.LEN tells our program
to consider only the first 5 characters of T1.VAL. Since T1.LEN is a variable,
we've simulated variable-length strings.
A minor disadvantage is that, for each object of type TEXT, we reserve enough
memory for the longest possible length (80 in this example). Discriminated
records, to be covered in the Advanced Topics section, can overcome this
disadvantage.
1HPlease type a space to go on, or B to go back. 2758 385B383$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Type TEXT will be much easier to use if we write some subprograms. First, we
need to convert between types TEXT and STRING. Conversion in both directions
is very simple:
function STR(T : in TEXT) return STRING is
begin
return T.VAL(1 .. T.LEN);
end STR;
function TXT(S : in STRING) return TEXT is
ANSWER : TEXT;
begin
ANSWER.LEN := S'LENGTH;
ANSWER.VAL(1 .. ANSWER.LEN) := S;
return ANSWER;
end TXT;
Now we can write, for example, T1 : TEXT := TXT("Hello"); and we don't even
have to count the characters of "Hello". Later, the program might execute
T1 := TXT("Type TEXT is very convenient."); showing that T1 simulates a
variable-length string. If we with and use TEXT_IO in our program, we can
write PUT_LINE(STR(T1));.
1HPlease type a space to go on, or B to go back. 2865 386B384$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$It would be convenient to overload the & operator to concatenate two TEXTs, or
a TEXT with a STRING. We can also overload four of the relational operators:
function "&" (LEFT, RIGHT : in TEXT) return TEXT;
function "&" (LEFT : in TEXT; RIGHT : in STRING) return TEXT;
function "&" (LEFT : in STRING; RIGHT : in TEXT) return TEXT;
function "<" (LEFT, RIGHT : in TEXT) return BOOLEAN;
function ">" (LEFT, RIGHT : in TEXT) return BOOLEAN;
function "<=" (LEFT, RIGHT : in TEXT) return BOOLEAN;
function ">=" (LEFT, RIGHT : in TEXT) return BOOLEAN;
The bodies of these subprograms are very simple! For example:
function "&"(LEFT, RIGHT : in TEXT) return TEXT is
begin
return TXT(STR(LEFT) & STR(RIGHT));
end "&";
function "<"(LEFT, RIGHT : in TEXT) return BOOLEAN is
begin
return STR(LEFT) < STR(RIGHT);
end "<";
1HPlease type a space to go on, or B to go back. 1830F387T388B385$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ function TXT(S : in STRING) return TEXT is
ANSWER : TEXT;
begin
ANSWER.LEN := S'LENGTH;
ANSWER.VAL(1 .. ANSWER.LEN) := S;
return ANSWER;
end TXT;
True or False? A call to TXT with the null string, TXT(""), will raise a
CONSTRAINT_ERROR.
1HPlease press T for true or F for false, or B to go back. 2133 389B386Q386$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ function TXT(S : in STRING) return TEXT is
ANSWER : TEXT;
begin
ANSWER.LEN := S'LENGTH;
ANSWER.VAL(1 .. ANSWER.LEN) := S;
return ANSWER;
end TXT;
You're right! The first executable statement will set ANSWER.LEN to 0, and
the second executable statement will do nothing. The check for
CONSTRAINT_ERROR is suppressed when a null slice is involved. Thus the
function will work correctly even for the null string.
1HPlease type a space to go on, or B or Q to go back to the question. 2071 389B386Q386$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ function TXT(S : in STRING) return TEXT is
ANSWER : TEXT;
begin
ANSWER.LEN := S'LENGTH;
ANSWER.VAL(1 .. ANSWER.LEN) := S;
return ANSWER;
end TXT;
False. The function will work correctly even for the null string. The first
executable statement will set ANSWER.LEN to 0, and the second executable
statement will do nothing. The check for CONSTRAINT_ERROR is suppressed when a
null slice is involved.
1HPlease type a space to go on, or B or Q to go back to the question. 3238 390B386$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type TEXT is
record
LEN : INTEGER range 0 .. 80 := 0;
VAL : STRING(1 .. 80);
end record;
There are two problems with type TEXT. The way Ada assigns arrays is less than
ideal when assigning objects of type TEXT, and the way Ada tests for equality
is totally unacceptable. Suppose we have T1, T2 : TEXT; and then we execute
T1 := TXT("Hello"); and then T2 := T1;. In doing the assignment, Ada will
copy all 80 characters of T1.VAL, even though the last 75 characters contain
garbage and only the first 5 characters (and the LEN field) need be copied.
Perhaps we could live with this inefficiency, but Ada's test for equality of
arrays creates a more serious problem.
Suppose we execute T1 := TXT("aaaaaaaaaa"); and T2 := TXT("bbbbbbbbbb"); and
then T1 := TXT("Hello"); and T2 := TXT("Hello");. If we now ask Ada to test
if T1 = T2, Ada will compare the entire VAL fields and conclude that T1 /= T2.
Note that T1.VAL(6 .. 10) is "aaaaa" while T2.VAL(6 .. 10) is "bbbbb", even
though only the first five characters of T1 and T2 should be considered. (Both
LEN fields are 5.) We of course want Ada to say that T1 = T2.
1HPlease type a space to go on, or B to go back. 2932 391B389$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$We could try to get around this problem by writing our own function EQUAL:
function EQUAL(T1, T2 : in TEXT) return BOOLEAN is
begin
return STR(T1) = STR(T2);
end EQUAL;
This function would work, but we might forget to write if EQUAL(T1, T2) and
write if T1 = T2 instead. Similarly, we could write a procedure to assign
TEXTs efficiently, and forget to use it and write T2 := T1;. But Ada will
prevent us from assigning TEXTs and testing them for equality if we create a
package and make type TEXT limited private. Outside the package there are
only two things we may do with objects of a limited private type:
1. Create them: T1, T2 : TEXT;
2. Use any procedures, functions, and infix operators provided by the package:
T1 := T1 & T2; etc.
We can't test for equality and inequality unless the package includes a
function "=". Also, Ada won't let us write T2 := T1;, but the package could
provide a procedure to assign TEXTs. Here's our package specification:
1HPlease type a space to go on, or B to go back. 2738 392B390$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ package TEXT_HANDLER is
type TEXT is limited private;
function STR(T : in TEXT) return STRING;
function TXT(S : in STRING) return TEXT;
function "&"(LEFT, RIGHT : in TEXT) return TEXT;
...
function "="(LEFT, RIGHT : in TEXT) return BOOLEAN;
function "<"(LEFT, RIGHT : in TEXT) return BOOLEAN;
...
procedure SET(TARGET : in out TEXT; TO : in TEXT);
private
type TEXT is
record
LEN : INTEGER range 0 .. 80 := 0;
VAL : STRING(1 .. 80);
end record;
end TEXT_HANDLER;
Note that we write type TEXT is limited private, but we still introduce the
private part of the package simply with the word private.
The two new subprograms are as easy to write as the others:
1HPlease type a space to go on, or B to go back. 2322 393B391$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ function "="(LEFT, RIGHT : in TEXT) return BOOLEAN is
begin
return STR(LEFT) = STR(RIGHT);
end "=";
procedure SET(TARGET : in out TEXT; TO : in TEXT) is
In summary, we used limited private for type TEXT because Ada's definitions of
equality and assignment weren't appropriate for that type, and we wanted to
provide our own definitions. However, Ada's definitions were appropriate for
SCREEN_TYPE, so we made that a private type to contain the effects of changes.
1HPlease type a space to go on, or B to go back. 262323941395B392$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ package STACKS is
type STACK is ?
procedure PUSH (S : in out STACK; ITEM : in INTEGER);
procedure POP (S : in out STACK; ITEM : out INTEGER);
private
type IVECTOR is array(INTEGER range <>) of INTEGER;
type STACK is
record
SP : INTEGER range 1 .. 11 := 1;
ST : IVECTOR(1 .. 10);
end record;
end STACKS;
Suppose we want to write a package that lets us create objects of type STACK,
and push and pop integers on them. The stacks will be last in, first out.
(For now, ignore the possibilities of stack underflow and overflow.) Should
type STACK be private or limited private?
1. Type STACK should be private.
2. Type STACK should be limited private.
1HPlease press 1 or 2, or B to go back. 2455 396B393Q393$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ package STACKS is
type STACK is limited private;
procedure PUSH (S : in out STACK; ITEM : in INTEGER);
procedure POP (S : in out STACK; ITEM : out INTEGER);
private
type IVECTOR is array(INTEGER range <>) of INTEGER;
type STACK is
record
SP : INTEGER range 1 .. 11 := 1;
ST : IVECTOR(1 .. 10);
end record;
end STACKS;
You're right! Similar to type TEXT, only part of the array in type STACK (the
part up through SP - 1) is significant; the rest contains garbage. Thus, Ada's
test for equality is unsatisfactory for the same reason, and type STACK should
be limited private.
1HPlease type a space to go on, or B or Q to go back to the question. 2422 396B393Q393$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ package STACKS is
type STACK is ?
procedure PUSH (S : in out STACK; ITEM : in INTEGER);
procedure POP (S : in out STACK; ITEM : out INTEGER);
private
type IVECTOR is array(INTEGER range <>) of INTEGER;
type STACK is
record
SP : INTEGER range 1 .. 11 := 1;
ST : IVECTOR(1 .. 10);
end record;
end STACKS;
No, type STACK is similar to type TEXT in that only part of the array (the part
up through SP - 1) is significant; the rest contains garbage. Thus, Ada's test
for equality is unsatisfactory for the same reason, and type STACK should be
limited private.
1HPlease type a space to go on, or B or Q to go back to the question. 2719 397B393$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ACCESS TYPES
Access types are sometimes called "pointers" in other languages. However, the
name "pointer" fell into disrepute, because of "dangling reference" problems.
With Ada's access types, dangling references are impossible, unless we
deliberately instantiate UNCHECKED_DEALLOCATION (discussed in the Advanced
Topics section).
If we define type DATE as before, and write USA : DATE; then USA has three
fields, DAY, MONTH, and YEAR. However, if we write
type P is access DATE;
D1, D2 : P;
then D1 and D2 are capable of pointing to objects of type DATE. In most
implementations of Ada, this means that D1 and D2 can each contain the machine
address of the start of a record of type DATE. However, this detail depends on
the implementation of Ada. D1 and D2 themselves don't have fields, but the
objects they can point to have fields.
1HPlease type a space to go on, or B to go back. 3160 398B396$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type P is access DATE;
D1, D2 : P;
At present D1 and D2 point to nothing. However, if we execute
D1 := new DATE;
then, at run time, enough memory for one record of type DATE is allocated, and
D1 is made to point to it. This new object of type DATE doesn't have a name;
only the pointer, D1, has a name. By contrast, when we elaborate USA : DATE;
we create an object that has a name (USA), but nothing can point to it.
We can refer to the fields of the nameless object pointed to by D1 as if D1
itself had fields. Thus, we can write D1.DAY, D1.MONTH, and D1.YEAR, and use
these on either side of an assignment: D1.DAY := 12;. The entire object
pointed to by D1 is D1.all, so we could write D1.all := (12, OCT, 1492);. Note
that D1.DAY is simply an abbreviation for D1.all.DAY.
We can execute D2 := new DATE'(4, JUL, 1776); giving the new object a value
when it's created. We can also declare D3 : P := new DATE; to make D3 point to
an object when D3 is created, or even write D3 : P := new DATE'(4, JUL, 1776);
to make D3 point to an object and also give the object a value.
1HPlease type a space to go on, or B to go back. 3419 399B397$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$We can write D3 := null; to make D3 point nowhere. When a pointer is declared
and no initialization is shown, it's automatically initialized to null, so
D4 : P; means D4 : P := null;. When a pointer is null, trying to reference the
object pointed to (D4.DAY or D4.all, etc.) will raise a CONSTRAINT_ERROR. We
can test to see if a pointer is null: if D4 = null then ...
Copying a pointer isn't the same as copying the object pointed to. If we
execute D1.all := (12, OCT, 1492); and then D2.all := D1.all;, the entire
record is copied. If we change D1.DAY with D1.DAY := 13;, D2.DAY is still 12.
However, is we execute D1.all := (12, OCT, 1492); and then D2 := D1;, then the
address in D1 is copied to D2, so that D2 now points to the same place as D1.
Thus, if we change D1.DAY with D1.DAY := 13;, then D2.DAY is also 13, because
it references the same memory location.
If we have D1 := new DATE'(12, OCT, 1492); and D2 := new DATE'(4, JUL, 1776);
and then execute D2 := D1;, D2 now points where D1 points, and nothing any
longer points to the object containing (4, JUL, 1776). Most systems will later
reclaim the memory occupied by that object, if the memory is needed. This
process is called garbage collection. It's automatic, and normally need not
concern the programmer.
1HPlease type a space to go on, or B to go back. 2971 400B398$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$A simple linked list can be thought of as a chain. Each link contains some
useful data (perhaps an integer, perhaps pages of information), and a pointer
to the next item in the chain. There's also a pointer, usually called HEAD,
that points to the first link in the chain. The last link points nowhere.
A linked list of integers might look something like this:
____ __________ __________ __________
| --|---->| INT 10 | ,--->| INT 27 | ,--->| INT 34 |
|____| | NEXT ---|---' | NEXT ---|---' | NEXT null|
HEAD |__________| |__________| |__________|
To add another integer to the chain, keeping the integers in ascending order,
we simply break the chain at the appropriate point and insert another link.
To set up our linked list, we'd like to write type P is access LINK; and write
type LINK is
record
INT : INTEGER;
NEXT : P;
end record;
1HPlease type a space to go on, or B to go back. 2716 401B399$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$However, the declaration of type P involves LINK, and the declaration of type
LINK involves P, so neither declaration can come first! Ada provides a special
means of solving this problem. We can write
type LINK;
type P is access LINK;
type LINK is
record
INT : INTEGER;
NEXT : P;
end record;
The first line is called an incomplete type declaration. It simply tells the
compiler that type LINK exists. That's all the information the compiler needs
to compile the second line. The second line tells the compiler that objects of
type P will contain pointers, but for this line the compiler doesn't need to
know details of the objects pointed to. The second line must be followed by
the full definition of type LINK.
1HPlease type a space to go on, or B to go back. 2222240214033403B400$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type PERSON is
record
NAME : STRING(1 .. 10);
AGE : NATURAL;
end record;
type P is access PERSON;
P1 : P := new PERSON'("Susan ", 21);
P2 : P := new PERSON'("John ", 35);
...
P2 := P1;
P1.AGE := 22;
What is P2.AGE?
1. P2.AGE is 21.
2. P2.AGE is 22.
3. P2.AGE is 35.
1HPlease press 1, 2, or 3, or B to go back. 2143 404B401Q401$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type PERSON is
record
NAME : STRING(1 .. 10);
AGE : NATURAL;
end record;
type P is access PERSON;
P1 : P := new PERSON'("Susan ", 21);
P2 : P := new PERSON'("John ", 35);
...
P2 := P1;
P1.AGE := 22;
You're right! The last line changed P1.AGE to 22. Since the previous line
made P2 point where P1 points, P2.AGE is also 22.
1HPlease type a space to go on, or B or Q to go back to the question. 2125 404B401Q401$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type PERSON is
record
NAME : STRING(1 .. 10);
AGE : NATURAL;
end record;
type P is access PERSON;
P1 : P := new PERSON'("Susan ", 21);
P2 : P := new PERSON'("John ", 35);
...
P2 := P1;
P1.AGE := 22;
No, the last line changed P1.AGE to 22. Since the previous line made P2 point
where P1 points, P2.AGE is also 22.
1HPlease type a space to go on, or B or Q to go back to the question. 3116 405B401$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Let's use an access type to write a program that gets integers in random order
from the terminal, maintaining a linked list of them. When 0 is input, the
program outputs the integers in ascending order. This program will be a good
stepping-stone to Outside Assignment 5. To simplify inserting an integer into
the linked list, HEAD will point to an unused LINK, which will in turn point to
the first actual link in the chain:
____ __________ __________ __________ __________
| --|--->| INT | ,-->| INT 10 | ,-->| INT 27 | ,-->| INT 34 |
|____| | NEXT ---|---' | NEXT ---|---' | NEXT ---|---' | NEXT null|
HEAD |__________| |__________| |__________| |__________|
We could create our linked list using arrays rather than an access type.
However, we'd have to specify the size of the arrays, placing a limit on the
number of integers the program can handle. With the access type, the only
limit is the amount of available memory. We'll be able to move our program to
a larger machine to increase this limit, without changing any code - not even
one line to specify the size of an array.
Here's our program ...
1HPlease type a space to go on, or B to go back. 2373 406B404$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO; use TEXT_IO;
procedure LL_DEMO is
package MY_INT_IO is new INTEGER_IO(INTEGER); use MY_INT_IO;
type LINK;
type P is access LINK;
type LINK is
record
INT : INTEGER;
NEXT : P;
end record;
HEAD : P := new LINK;
I : INTEGER;
procedure ADD_I_TO_LINKED_LIST is separate;
procedure DISPLAY_LINKED_LIST is separate;
begin
PUT("Type an integer: "); GET(I);
while I /= 0 loop
ADD_I_TO_LINKED_LIST;
PUT("Type an integer: "); GET(I);
end loop;
DISPLAY_LINKED_LIST;
end LL_DEMO;
1HPlease type a space to go on, or B to go back. 2756 407B405$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ separate (LL_DEMO)
procedure DISPLAY_LINKED_LIST is
TMP : P := HEAD.NEXT; -- Skip unused link at the head of the list.
begin
while TMP /= null loop
PUT(TMP.INT); NEW_LINE; -- Print integer in the current link.
TMP := TMP.NEXT; -- Go to next link in the list.
end loop;
end DISPLAY_LINKED_LIST;
separate (LL_DEMO)
procedure ADD_I_TO_LINKED_LIST is
TMP : P := HEAD; -- Begin search of where to insert at start of list.
begin
while TMP /= null and then TMP.NEXT /= null and then TMP.NEXT.INT < I loop
TMP := TMP.NEXT; -- Note use of "and then" to avoid trying to reference
end loop; -- the object pointed to when the pointer is null.
TMP.NEXT := new LINK'(I, TMP.NEXT); -- Create new link and insert in list.
end ADD_I_TO_LINKED_LIST;
The best way to follow these two subprograms is to draw a linked list on a
piece of scrap paper and "hand execute" the subprograms.
1HPlease type a space to go on, or B to go back. 194744081409241034115412B406$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type LINK; -- 1
type P is access LINK;
type LINK is
record
F : FLOAT; -- 2
S : STRING(1 .. 10); -- 3
A : array(1 .. 10) of INTEGER; -- 4
end record;
L1 : LINK; -- 5
P1 : P;
Which commented line in the above is illegal?
1HPlease press 1, 2, 3, 4, or 5, or B to go back. 2350 413B407Q407$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type LINK; -- 1
type P is access LINK;
type LINK is
record
F : FLOAT; -- 2
S : STRING(1 .. 10); -- 3
A : array(1 .. 10) of INTEGER; -- 4
end record;
L1 : LINK; -- 5
P1 : P;
You're right! Inside a record definition, the name of a field must be
followed by a type name, not array. We would first have to say something like
type LINE is array(INTEGER range <>) of INTEGER;, and then change the field
definition to
A : LINE(1 .. 10);
1HPlease type a space to go on, or B or Q to go back to the question. 2121 413B407Q407$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type LINK; -- 1
type P is access LINK;
type LINK is
record
F : FLOAT; -- 2
S : STRING(1 .. 10); -- 3
A : array(1 .. 10) of INTEGER; -- 4
end record;
L1 : LINK; -- 5
P1 : P;
No, the first line is legal. Ada allows an incomplete type declaration to
precede the declaration of an access type, provided the complete type
declaration follows.
1HPlease type a space to go on, or B or Q to go back to the question. 2034 413B407Q407$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type LINK; -- 1
type P is access LINK;
type LINK is
record
F : FLOAT; -- 2
S : STRING(1 .. 10); -- 3
A : array(1 .. 10) of INTEGER; -- 4
end record;
L1 : LINK; -- 5
P1 : P;
No, the declaration of F inside the record definition is legal, because Ada
records may contain fields of any type.
1HPlease type a space to go on, or B or Q to go back to the question. 2034 413B407Q407$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type LINK; -- 1
type P is access LINK;
type LINK is
record
F : FLOAT; -- 2
S : STRING(1 .. 10); -- 3
A : array(1 .. 10) of INTEGER; -- 4
end record;
L1 : LINK; -- 5
P1 : P;
No, the declaration of S inside the record definition is legal, because Ada
records may contain fields of any type.
1HPlease type a space to go on, or B or Q to go back to the question. 2260 413B407Q407$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type LINK; -- 1
type P is access LINK;
type LINK is
record
F : FLOAT; -- 2
S : STRING(1 .. 10); -- 3
A : array(1 .. 10) of INTEGER; -- 4
end record;
L1 : LINK; -- 5
P1 : P;
No, the declaration of L1 as type LINK is legal. Even though type P was
declared to be access LINK and we ordinarily would declare objects to be of
type P, we may also directly declare objects to be of type LINK. Of course,
nothing can point to such objects.
1HPlease type a space to go on, or B or Q to go back to the question. 3172 414B407$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ USER DEFINED TYPES AND PORTABILITY
In some implementations of Ada, INTEGERs are represented by 32-bit two's
complement numbers, giving the range -2_147_483_648 .. 2_147_483_648. Other
implementations use 16-bit two's complement INTEGERs, giving the range
-32768 .. 32767. However, some of the 16-bit implementations also provide a
32-bit type called LONG_INTEGER.
Suppose we need a variable called NUMBER to count from zero to one million. We
could declare NUMBER: INTEGER; for the 32-bit implementation, and change this
to NUMBER : LONG_INTEGER; when we port the program to a machine running a
16-bit version that provides LONG_INTEGER. However, we could also declare
type COUNTER is range 0 .. 1_000_000;
NUMBER : COUNTER;
and both implementations of Ada will automatically select the appropriate
internal representation for our type COUNTER! The 32-bit Ada will select
INTEGER, and the 16-bit Ada will select LONG_INTEGER. This gives us the
advantage that no code has to be changed when the program is ported. COUNTER
is called a user-defined type. Of course, we must use explicit type
conversion to mix objects of type COUNTER with objects of other types.
1HPlease type a space to go on, or B to go back. 2657 415B413$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Similarly, different implementations of Ada provide different representations
for type FLOAT, and some provide a type LONG_FLOAT. We can declare
type REAL is digits 8;
F : REAL;
and be certain that F will have at least 8 digits of accuracy on any machine
that accepts this type declaration. A range constraint is optional.
User defined types also apply to fixed point numbers; these will be discussed
in the Advanced Topics section.
It's possible to make a declaration that will be accepted by only some
implementations of Ada. For example, if we declare
type X is digits 30 range 0.0 .. 100.0;
some implementations of Ada might have to report that there's no available type
that gives at least 30 digits of accuracy.
No language can give perfectly portable programs, but Ada truly advanced the
state of the art in portability.
1HPlease type a space to go on, or B to go back. 2839 416B414$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ DERIVED TYPES
Derived types are created to prevent accidental mixing of objects. Unlike
subtypes, derived types are separate types. For example,
type NO_OF_APPLES is new INTEGER;
type NO_OF_ORANGES is new INTEGER range 0 .. 100;
NOA : NO_OF_APPLES;
NOO : NO_OF_ORANGES;
I : INTEGER;
...
NOA := NOA + NOO; -- Illegal
NOA := NOA + NOA;
NOA := NOA + I; -- Illegal
NOA := NOA + NO_OF_APPLES(I);
NOA := NOA + NO_OF_APPLES(NOO);
A derived type is denoted by the reserved word new followed by an existing type
like INTEGER. The operations that Ada knows for INTEGERs, such as addition,
are "inherited" by the derived types so that, for example, Ada knows how to add
two objects of type NO_OF_ORANGES. As the examples above show, we can't mix
types accidentally, but we can deliberately mix them by converting first.
1HPlease type a space to go on, or B to go back. 2073 417B415$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$In summary,
Subtypes are usually created to provide range constraints:
subtype DAY_SUBTYPE is INTEGER range 1 .. 31;
Derived types are usually created to prevent accidental mixing:
type NO_OF_APPLES is new INTEGER;
type NO_OF_ORANGES is new INTEGER ... ;
User-defined types are usually created to gain portability:
type COUNTER is range 0 .. 1_000_000;
type REAL is digits 8;
1HPlease type a space to go on, or B to go back. 2325141824193420B416$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type METERS is new FLOAT;
type SECONDS is new FLOAT;
type METERS_PER_SECOND is new FLOAT;
...
function "*"(LEFT : in METERS_PER_SECOND; RIGHT : in SECONDS) return METERS is
begin
return METERS(LEFT) * METERS(RIGHT);
end "*";
function "*"(LEFT : in SECONDS; RIGHT : in METERS_PER_SECOND) return METERS is
begin
return RIGHT * LEFT;
end "*";
function "/"(LEFT : in METERS; RIGHT : in SECONDS) return METERS_PER_SECOND is
1HPlease press 1, 2, or 3, or B to go back. 1626 421B417Q417$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type METERS is new FLOAT;
type SECONDS is new FLOAT;
type METERS_PER_SECOND is new FLOAT;
You're right! The reserved word new in the segment above tells us that we're
defining derived types.
1HPlease type a space to go on, or B or Q to go back to the question. 1637 421B417Q417$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type METERS is new FLOAT;
type SECONDS is new FLOAT;
type METERS_PER_SECOND is new FLOAT;
No, examples of user-defined types are
type COUNTER is range 0 .. 1_000_000;
type REAL is digits 8;
1HPlease type a space to go on, or B or Q to go back to the question. 1515 421B417Q417$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type METERS is new FLOAT;
type SECONDS is new FLOAT;
type METERS_PER_SECOND is new FLOAT;
No, subtypes are usually created to provide range constraints.
1HPlease type a space to go on, or B or Q to go back to the question. 2744 422B417$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ EXCEPTIONS
When an error occurs during the elaboration or execution of a statement, Ada is
said to raise an exception. Ordinarily this stops the program, but Ada
programs can trap exceptions and execute a special block of code when one
occurs. This code is called an exception handler.
We can define our own exceptions, but five of them are predefined by Ada:
CONSTRAINT_ERROR
This is the exception encountered most often by beginners, because it can
be caused by a number of different things. It can be raised by a subscript out
of range, a subtype out of range (USA.DAY := 32;), an attribute used improperly
(INTEGER'VALUE("12X3") or MONTH_TYPE'VAL(13)), assigning an array of one length
to a destination of another (H : STRING(1 .. 5) := "Hi";), or by attempting to
access an object with a null pointer.
NUMERIC_ERROR
This can be raised by an arithmetic overflow, or by an attempt to divide
by zero.
1HPlease type a space to go on, or B to go back. 2931 423B421$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$PROGRAM_ERROR
This is rarely encountered by beginners, but it can be raised by skipping
around the return statement in a function and running into the end statement.
STORAGE_ERROR
This is raised by running out of memory, as with a recursive program
calling itself unconditionally or an attempt to create an infinitely large
linked list.
TASKING_ERROR
This will be discussed in the section on Tasking.
An exception handler is introduced by the reserved word exception; its
structure is similar to that of a case construct. We'll see an example in a
moment. Unlike a case construct, an exception handler need not account for all
the possibilities.
An exception handler can be placed in a subprogram, in the initialization code
of a package, in a task (to be discussed in the section on Tasking), or in a
block (to be discussed later in this section). Here's a procedure with an
exception handler that handles an exception, WRONG, that we declare ourselves,
as well as the built-in exception NUMERIC_ERROR:
1HPlease type a space to go on, or B to go back. 2417 424B422$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO; use TEXT_IO;
procedure EXCEPTION_DEMO is
package MY_INT_IO is new INTEGER_IO(INTEGER); use MY_INT_IO;
I : INTEGER;
WRONG : exception;
begin
loop
NEW_LINE(2); PUT("Type a positive integer. "); GET(I);
if I <= 0 then
raise WRONG;
end if;
PUT("The square is ... ");
PUT(I*I); -- Will raise NUMERIC_ERROR if I is too large.
end loop;
exception
when NUMERIC_ERROR =>
PUT(" ... too big.");
when WRONG =>
NEW_LINE;
PUT("I said POSITIVE integer!");
end EXCEPTION_DEMO;
1HPlease type a space to go on, or B to go back. 3719 425B423$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$We can deliberately raise an exception (either user-defined or built-in) with
the raise statement, as in raise WRONG; or raise CONSTRAINT_ERROR;. Also,
ordinary statements can raise exceptions. In our sample program, PUT(I*I);
raises NUMERIC_ERROR if I is too large. When an executable statement raises an
exception, the exception handler is executed instead of the rest of the
procedure, function, etc. Our program keeps asking for integers and printing
their squares until an exception is raised. Then, the exception handler is
executed, and there's no way to get back into the procedure to ask for another
integer (short of a recursive call). Even a goto from the exception handler to
the main part of the procedure is forbidden. Very soon we'll show how the
block construct can overcome this problem, so that our program will continue to
ask for more integers even after an exception is handled.
As with case constructs, an exception handler may use the vertical bar to
and it may say when others => to handle all cases not covered earlier. But
there's no way to test, inside the exception handler, which line raised the
exception. We can only test which kind of exception was raised.
Don't use exceptions where a simple if will do. In our program, trapping the
arithmetic overflow was OK, but the if could have handled I <= 0 without
raising WRONG. This exception was declared only to give a simple example.
1HPlease type a space to go on, or B to go back. 183614262427342844295430B424$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. CONSTRAINT_ERROR
2. NUMERIC_ERROR
3. PROGRAM_ERROR
4. STORAGE_ERROR
5. TASKING_ERROR
Assuming RAINBOW_COLOR and TRAFFIC_LIGHT_COLOR are defined as before, which of
the above exceptions would be raised by RAINBOW_COLOR'VALUE("AMBER")?
1HPlease press 1, 2, 3, 4, or 5, or B to go back. 1764 431B425Q425$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. CONSTRAINT_ERROR
2. NUMERIC_ERROR
3. PROGRAM_ERROR
4. STORAGE_ERROR
5. TASKING_ERROR
You're right! Using an attribute improperly in this way will raise
CONSTRAINT_ERROR.
1HPlease type a space to go on, or B or Q to go back to the question. 1755 431B425Q425$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. CONSTRAINT_ERROR
2. NUMERIC_ERROR
3. PROGRAM_ERROR
4. STORAGE_ERROR
5. TASKING_ERROR
No, NUMERIC_ERROR is usually raised by arithmetic overflow, or attempted
division by zero.
1HPlease type a space to go on, or B or Q to go back to the question. 1762 431B425Q425$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. CONSTRAINT_ERROR
2. NUMERIC_ERROR
3. PROGRAM_ERROR
4. STORAGE_ERROR
5. TASKING_ERROR
No, PROGRAM_ERROR is usually raised by skipping around the return statement in
a function.
1HPlease type a space to go on, or B or Q to go back to the question. 1717 431B425Q425$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. CONSTRAINT_ERROR
2. NUMERIC_ERROR
3. PROGRAM_ERROR
4. STORAGE_ERROR
5. TASKING_ERROR
No, STORAGE_ERROR is raised by running out of memory.
1HPlease type a space to go on, or B or Q to go back to the question. 1756 431B425Q425$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. CONSTRAINT_ERROR
2. NUMERIC_ERROR
3. PROGRAM_ERROR
4. STORAGE_ERROR
5. TASKING_ERROR
No, TASKING_ERROR is raised only by programs using tasking, which we haven't
yet discussed.
1HPlease type a space to go on, or B or Q to go back to the question. 2914 432B425$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$A block construct lets us declare objects in the executable region of the
program. For example, in the following, I and F come into existence where
they're declared, and go out of existence at the following end statement:
procedure BLOCK_DEMO is
Q : FLOAT;
begin
Q := 0.0;
declare
I : INTEGER;
F : FLOAT;
begin
I := 5;
F := Q;
end;
Q := Q + 3.0;
end BLOCK_DEMO;
However, the usual use of a block is to localize an exception handler, not to
bring objects into existence in the executable region of a program. The
declarative part of the block is optional. For example, let's rewrite
EXCEPTION_DEMO to make use of a block with an exception handler.
1HPlease type a space to go on, or B to go back. 2372 433B431$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO; use TEXT_IO;
procedure EXCEPTION_DEMO is
package MY_INT_IO is new INTEGER_IO(INTEGER); use MY_INT_IO;
I : INTEGER;
WRONG : exception;
begin
loop
begin
NEW_LINE(2); PUT("Type a positive integer. "); GET(I);
if I <= 0 then
raise WRONG;
end if;
PUT("The square is ... ");
PUT(I*I);
exception
when NUMERIC_ERROR =>
PUT(" ... too big.");
when WRONG =>
NEW_LINE; PUT("I said POSITIVE integer!");
end;
end loop;
end EXCEPTION_DEMO;
1HPlease type a space to go on, or B to go back. 3532 434B432$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Note that in our rewritten program, a block with an exception handler has been
created inside the loop. Now, if an exception occurs, the handler will be
executed instead of the rest of the block, not the rest of the procedure.
Thus, the loop will still be executed, and the program will continue to ask
for integers after an exception is handled.
There are two advantages to confining exception handlers to small blocks.
First, we narrow down the range of statements that might have raised the
exception. Recall that the handler can't test which line raised the exception,
but it must have been one of the lines in the block. (If an exception is
raised outside the block, our program provides no handler for it.) Second,
program execution will continue after the end of the block.
If an exception occurs for which there's no handler, the exception reaches the
next higher level. For example, if the block in EXCEPTION_DEMO somehow raises
STORAGE_ERROR and doesn't handle it, an exception handler for the whole
procedure would get a chance to handle it. (In our case, there is none.) If
it's still unhandled, it's as if the call to EXCEPTION_DEMO raised
STORAGE_ERROR. If the caller doesn't handle it, the exception reaches the
caller's caller, etc. If the exception reaches the main program and is still
unhandled, the program is stopped and the system prints the name of the
exception. However, exceptions that are handled don't reach the caller.
1HPlease type a space to go on, or B to go back. 3134 435B433$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$In the unusual case of an exception raised in the declarative region, the unit
raising the exception (subprogram, block, etc.) is not given a chance to handle
it. Exceptions raised in the declarative region immediately reach the next
higher level.
In a handler, the word raise may be used without a name of an exception to
re-raise whatever exception brought control to the handler. This is especially
useful after when others =>, because any one of a number of exceptions might
have transferred control there. For example,
when others =>
PUT_LINE("I don't know what went wrong.");
-- Close files and do general cleanup.
raise;
This lets us do some processing of the error, and still lets the next higher
level do additional processing. Note that it's superfluous to say simply when
others => raise; because the exception will reach the next higher level even if
that code is omitted. Any unhandled exception reaches the next higher level.
An error occurring in an exception handler is unhandled and reaches the next
higher level (unless it occurs in a block with its own exception handler).
1HPlease type a space to go on, or B to go back. 234524361437B434$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO; use TEXT_IO; separate (ONE)
procedure ONE is procedure TWO is
procedure TWO is separate; CAIN : exception;
begin begin
TWO; raise CAIN;
exception exception
when others => when others =>
PUT_LINE("1"); PUT_LINE("2");
end ONE; end TWO;
What will the above program print?
1. The program will print 1.
2. The program will print 2.
1HPlease press 1 or 2, or B to go back. 2264 438B435Q435$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO; use TEXT_IO; separate (ONE)
procedure ONE is procedure TWO is
procedure TWO is separate; CAIN : exception;
begin begin
TWO; raise CAIN;
exception exception
when others => when others =>
PUT_LINE("1"); PUT_LINE("2");
end ONE; end TWO;
You're right! TWO handles the exception, so it never reaches ONE.
1HPlease type a space to go on, or B or Q to go back to the question. 2225 438B435Q435$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO; use TEXT_IO; separate (ONE)
procedure ONE is procedure TWO is
procedure TWO is separate; CAIN : exception;
begin begin
TWO; raise CAIN;
exception exception
when others => when others =>
PUT_LINE("1"); PUT_LINE("2");
end ONE; end TWO;
No, TWO handles the exception, so it never reaches ONE.
1HPlease type a space to go on, or B or Q to go back to the question. 3125 439B435$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ MORE ABOUT TEXT_IO
We're almost ready for Outside Assignment 5. For that assignment, we had to
cover type TEXT, access types, and exceptions. We also need to learn a little
more about TEXT_IO before we do the assignment.
TEXT_IO is used for input and output to and from text files as well as input
and output to and from the terminal. Text files are files that can be listed
at the terminal. (Binary files and random access files are handled with the
packages SEQUENTIAL_IO and DIRECT_IO, which will be discussed in the Advanced
Topics section.)
The full specification of TEXT_IO appears in section 14.3.10 of the LRM. It's
rather long, and some of the procedures and functions are rarely used. So
there's a simplified specification of TEXT_IO in your printed course notes,
starting on page 16. Please consult your printed course notes during the
following discussion.
Note that there's a limited private type called FILE_TYPE. For each file that
our program will use, we must create an object of this type, for example,
F1, F2 : FILE_TYPE;. We can then use these objects in the procedures CREATE
and OPEN, to associate file names with the objects of type FILE_TYPE.
1HPlease type a space to go on, or B to go back. 3426 440B438$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Note that the I/O procedures, such as NEW_LINE, PUT, and GET, have one version
for use with the terminal, and another version for use with a file. The file
version takes an object of type FILE_TYPE, not the name of the file. The file
must first have been CREATEd or OPENed.
The exception STATUS_ERROR is raised by trying to do I/O on a closed file.
MODE_ERROR is raised by trying to read from a file opened or created with mode
OUT_FILE, or by trying to write to a file of mode IN_FILE. NAME_ERROR is
raised by trying to OPEN a file that doesn't exist, or by trying to CREATE a
file with a name not allowed by the system. END_ERROR is raised by trying to
read past an end-of-file.
NEW_LINE creates one or more blank lines on output, and SKIP_LINE skips one or
more lines of input. We can PUT characters and strings, and we can PUT_LINE a
string. We can GET a character, and GET_LINE a string. Note that when we
GET_LINE a string, the procedure returns the number of characters that were in
the line. Thus, if we have S : STRING(1 .. 80); LEN : INTEGER; and we execute
GET_LINE(S, LEN); and the user types Hello, LEN will be set to 5, S(1 .. 5)
will be set to "Hello", and the rest of S will be unmodified.
The generic package INTEGER_IO can be instantiated for any integer type,
including user-defined types and derived types like COUNTER and NO_OF_APPLES.
1HPlease type a space to go on, or B to go back. 2825 441B439$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$When we call PUT in our instantiation of INTEGER_IO, we can optionally specify
the width and the base.
The generic package FLOAT_IO can be instantiated for any floating point type,
such as FLOAT and the user-defined type REAL that we created earlier. PUT
allows us optionally to specify the number of places before and after the
decimal point, and the size of the optional exponent field.
The generic package ENUMERATION_IO can be instantiated for any enumeration
type. PUT allows us optionally to specify the width.
TEXT_IO contains another generic package FIXED_IO for fixed point types, not
shown in our simplified version. Fixed point types will be discussed in the
Advanced Topics section.
To illustrate the use of TEXT_IO, here's a simple program that prompts for the
names of an old input file and a new output file, and copies the input file to
the output. It's assumed that the input file is an ASCII text file no wider
than 80 characters, and that it contains no special control characters such as
form feeds:
1HPlease type a space to go on, or B to go back. 2555 442B440$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO; use TEXT_IO;
The statements CLOSE(F1); and CLOSE(F2); aren't really necessary, because all
files are automatically closed when the main program terminates.
This program also appears on page 18 of your printed course notes.
1HPlease type a space to go on, or B to go back. 2525 443B441$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$As this is written (March, 1988), there's no way to test whether a file already
exists without trying to OPEN it and trapping the NAME_ERROR if the file
doesn't exist. There's talk of enhancing TEXT_IO with the next revision of the
Ada standard.
The following function determines whether a file name represents an existing
file. It appears on page 18 of your printed course notes:
with TEXT_IO; use TEXT_IO;
function EXISTS(FILE_NAME : in STRING) return BOOLEAN is
F : FILE_TYPE;
ANSWER : BOOLEAN := TRUE;
begin
begin
OPEN(F, IN_FILE, S);
CLOSE(F);
exception
when NAME_ERROR => ANSWER := FALSE;
end;
return ANSWER;
end EXISTS;
1HPlease type a space to go on, or B to go back. 16211444244534464447B442$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. STATUS_ERROR
2. MODE_ERROR
3. NAME_ERROR
4. END_ERROR
Which exception is raised by attempting to do I/O on a closed file?
1HPlease press 1, 2, 3, or 4, or B to go back. 1664 448B443Q443$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. STATUS_ERROR
2. MODE_ERROR
3. NAME_ERROR
4. END_ERROR
You're right! STATUS_ERROR is raised by an attempt to do I/O on a closed file.
1HPlease type a space to go on, or B or Q to go back to the question. 1720 448B443Q443$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. STATUS_ERROR
2. MODE_ERROR
3. NAME_ERROR
4. END_ERROR
No, MODE_ERROR is raised by an attempt to read from a file of mode OUT_FILE, or
write to a file of mode IN_FILE.
1HPlease type a space to go on, or B or Q to go back to the question. 1720 448B443Q443$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. STATUS_ERROR
2. MODE_ERROR
3. NAME_ERROR
4. END_ERROR
No, NAME_ERROR is raised by an attempt to OPEN a file that doesn't exist, or
CREATE a file with an illegal name.
1HPlease type a space to go on, or B or Q to go back to the question. 1637 448B443Q443$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. STATUS_ERROR
2. MODE_ERROR
3. NAME_ERROR
4. END_ERROR
No, END_ERROR is raised by an attempt to read past an end-of-file.
1HPlease type a space to go on, or B or Q to go back to the question. 3440 449B443$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ OUTSIDE ASSIGNMENT 5 - WRITING A SIMPLE LINE EDITOR
We're finally ready for the next Outside Assignment! This assignment will give
you a chance to write a program of greater complexity than the previous
assignments. By the time you've completed Outside Assignment 5, you should
feel comfortable with Ada. The full set of requirements for the line editor we
want you to write are in your printed course notes, starting on page 19. We'll
discuss them briefly here. No test driver is supplied, but after you've
written the program, we'll give you some tests to perform manually on your line
editor. You've completed the assignment when your editor passes all the tests.
Imagine that your screen editor is unavailable to a particular user, perhaps
because he's dialing your computer from a remote location, and your screen
editor writes directly to the screen. You want to write a line editor. While
your computer already has a line editor called EDLIN, it's difficult to learn
to use. The line editor you'll write, called LEDIT, will take almost no effort
to learn. The only commands are LIST and EXIT.
The user begins each line of text with a line number, similar to Basic. Line
numbers must be integers between 1 and 29999. Regardless of the order in which
lines are entered, LEDIT maintains a linked list of lines in order by number,
so that it can LIST the text in order, or write it to a file.
1HPlease type a space to go on, or B to go back. 1974 450B448$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Line numbers need not be consecutive, and they may be preceded by any number of
spaces. For example, if we type
40 -- This is a comment.
20 begin
10 with TEXT_IO; use TEXT_IO;
30 end ADD;
and then type LIST, the editor displays
10 with TEXT_IO; use TEXT_IO;
20 begin
30 end ADD;
40 -- This is a comment.
To insert lines, we merely type lines with intermediate line numbers. In our
example, if we now type
15 procedure HELLO is
LIST
we'll see
1HPlease type a space to go on, or B to go back. 1863 451B449$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 10 with TEXT_IO; use TEXT_IO;
15 procedure HELLO is
20 begin
30 end ADD;
40 -- This is a comment.
To replace a line, we simply retype the line with the same line number as the
line to be replaced, and to delete a line, we type only the line number. If
we now type
15 procedure ADD is
40
LIST
we'll see
10 with TEXT_IO; use TEXT_IO;
15 procedure ADD is
20 begin
30 end ADD;
1HPlease type a space to go on, or B to go back. 2523 452B450$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Thus the user can insert, replace, and delete lines by line numbers, without
learning any commands! If the user forgets the space after the line number, no
harm is done; LEDIT takes 20begin the same as 20 begin. However, the user can
indent code by adding extra spaces after the line number. The following
example has three extra spaces after each line number (four spaces total):
24 PUT(2 + 2);
26 NEW_LINE;
18 package MY_INT_IO is new INTEGER_IO(INTEGER); use MY_INT_IO;
LIST
10 with TEXT_IO; use TEXT_IO;
15 procedure ADD is
18 package MY_INT_IO is new INTEGER_IO(INTEGER); use MY_INT_IO;
20 begin
24 PUT(2 + 2);
26 NEW_LINE;
30 end ADD;
When LISTing, LEDIT always allows exactly five spaces for the line number, so
that the text lines up correctly:
1HPlease type a space to go on, or B to go back. 2430 453B451$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$LIST
2 This is a sample listing,
20 showing how the text
200 lines up, even when
2000 some line numbers are
20000 longer than others.
When we type EXIT, LEDIT writes the output file without the line numbers. The
text above would all start in column 1 of the output file.
For LEDIT to be useful with files larger than a page, it must be possible to
list a range of lines:
LIST 20 - 30
This is only a summary of the requirements for LEDIT. Please refer to pages
19-23 of your printed course notes for the actual requirements. As a point of
reference, our solution requires about 180 lines of Ada on four pages.
Here are the steps to follow for Outside Assignment 5. They can also be found
in the printed course notes on page 24:
1HPlease type a space to go on, or B to go back. 2415 454B452$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$1. Carefully read the requirements starting on page 19 of the printed course
notes. Take your time.
2. Write the Ada code, compile, and link. Call the main program LEDIT. If
you have any questions about what LEDIT should do, you can compile and run
our solution, which is in LEDIT.ANS.
3. Refer to pages 25-27 of the printed course notes for instructions on
testing your line editor. If any tests are failed, go back to step 2.
4. When all the tests are passed, you've completed the assignment and will
have a chance to compare your solution with ours.
Please type X to exit ADA-TUTR temporarily, and try Outside Assignment 5. It
will probably take several days. Take your time; there's no deadline. Good
luck!
1HPlease type X to exit, a space to go on, or B to go back. 2130 455B453$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Congratulations on Completing Outside Assignment 5!
If you like, you can compare your solution with ours, which is in LEDIT.ANS. A
listing starts on page 28 of your printed course notes. Note that a single
procedure handles adding, deleting, and replacing lines. Replacing is done by
first deleting, then adding a line.
Your solution might be very different from ours, but if it passed all the
tests, consider it correct.
Early in this course we used the generic package INTEGER_IO. Let's now learn
how to write our own generic packages, procedures, and functions.
1HPlease type a space to go on, or B to go back. 2913 456B454$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ GENERICS
It would be easy to write a package that creates a single stack of 10 INTEGERs,
and lets us PUSH and POP on it. However, the code would be about the same
regardless of the size of the stack, and regardless of the type of objects on
the stack. For example, a second package that creates a stack of 50 DATEs
would look about the same. We can write one generic package, and instantiate
it for any size and almost any type we need. The specification is:
generic
SIZE : POSITIVE;
type DUMMY is private;
package STACK_PACKAGE is
procedure PUSH(OBJECT : in DUMMY);
function POP return DUMMY;
end STACK_PACKAGE;
Since both SIZE and type DUMMY are generic, both must be specified when we
instantiate the package:
package STACK_OF_10_INTEGERS is new STACK_PACKAGE(10, INTEGER);
package STACK_OF_50_DATES is new STACK_PACKAGE(50, DATE);
1HPlease type a space to go on, or B to go back. 3574 457B455$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$If the generic part says type DUMMY is private; then the subprogram or package
body may do only three things with objects of type DUMMY: create them, assign
them, and test them for equality or inequality. Note that this is similar to
the list of things we may do with a private type outside an ordinary package.
In this case, we're listing what we can do inside the generic package. Calling
the package's subprograms isn't on our list, because they're not written yet!
We're listing the things we can do when we write the body of the package. Of
course, once we write some subprograms, other subprograms can call them.
Although we can do only three things with objects of a private type inside a
generic package, we can instantiate that package with almost any type at all.
Our STACK_PACKAGE can be instantiated for DATEs, STRINGs (see the next
paragraph), RAINBOW_COLORs, FLOATs, and any type for which we can assign
objects and test them for equality. That means any type except a limited
private type. We can't instantiate STACK_PACKAGE for type TEXT_IO.FILE_TYPE
(a limited private type), because in our package we're allowed to assign and
test for equality objects of type DUMMY.
Since we can't create objects of an unconstrained array type, if we want to
instantiate STACK_PACKAGE for STRINGs, we must use a constrained subtype of
STRING. For example, we could write subtype NAME is STRING(1 .. 30); and then
write package STACK_OF_50_NAMES is new STACK_PACKAGE(50, NAME);.
1HPlease type a space to go on, or B to go back. 3136 458B456$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$We could instantiate our package even for limited private types like
TEXT_IO.FILE_TYPE if the generic part had said type DUMMY is limited private;.
However, the only thing our package could do with objects of that type is
create them. Ordinarily, that's not very useful.
If the generic part says type DUMMY is ( <> ); then we can instantiate the
package (or subprogram) for any discrete type. That means any enumeration or
integer type: CHARACTER, BOOLEAN, COUNTER, NO_OF_APPLES, etc. Inside the
package, attributes like 'FIRST are available.
If the generic part says type DUMMY is range <>; then we can instantiate for
any integer type. Inside the package, we can do things that can be done with
all integer types, such as add, etc.
If the generic part says type DUMMY is delta <>; then we can instantiate for
any fixed point type, to be discussed in the Advanced Topics section.
Finally, if the generic part says type DUMMY is digits <>; then we can
instantiate for any floating point type, such as FLOAT or the REAL we created.
The specification of a generic subprogram must be given separately from the
body. For example, we can't eliminate the highlighted line in this function:
1HPlease type a space to go on, or B to go back. 2374 459B457$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ generic
type DUMMY_FLOAT is digits <>;
type DUMMY_VECTOR is array (INTEGER range <>) of DUMMY_FLOAT;
function SUM(V : in DUMMY_VECTOR) return DUMMY_FLOAT;
function SUM(V : in DUMMY_VECTOR) return DUMMY_FLOAT is
ANSWER : DUMMY_FLOAT := 0.0;
begin
for I in V'RANGE loop
ANSWER := ANSWER + V(I);
end loop;
return ANSWER;
end SUM;
We can instantiate SUM and call it as follows:
type VECTOR is array(INTEGER range <>) of FLOAT;
V1 : VECTOR(1 .. 10);
X : FLOAT;
function SUMV is new SUM(FLOAT, VECTOR);
...
X := SUMV(V1);
1HPlease type a space to go on, or B to go back. 2024346014612462B458$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ generic
type DUMMY is range <>;
procedure DISPLAY(ITEM : in DUMMY);
type NO_OF_APPLES is new INTEGER;
type COUNTER is range 0 .. 1_000_000;
type ANSWER is (YES, NO, MAYBE);
procedure DISPLAY_APPLES is new DISPLAY(NO_OF_APPLES); -- 1
procedure DISPLAY_COUNTERS is new DISPLAY(COUNTER); -- 2
procedure DISPLAY_ANSWERS is new DISPLAY(ANSWER); -- 3
Which commented line in the above program segment is illegal?
1HPlease press 1, 2, or 3, or B to go back. 2243 463B459Q459$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ generic
type DUMMY is range <>;
procedure DISPLAY(ITEM : in DUMMY);
type NO_OF_APPLES is new INTEGER;
type COUNTER is range 0 .. 1_000_000;
type ANSWER is (YES, NO, MAYBE);
procedure DISPLAY_APPLES is new DISPLAY(NO_OF_APPLES); -- 1
procedure DISPLAY_COUNTERS is new DISPLAY(COUNTER); -- 2
procedure DISPLAY_ANSWERS is new DISPLAY(ANSWER); -- 3
You're right! When the generic part says type DUMMY is range <>;, the
procedure may be instantiated for any integer type. ANSWER is a discrete type,
but not an integer type.
1HPlease type a space to go on, or B or Q to go back to the question. 2236 463B459Q459$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ generic
type DUMMY is range <>;
procedure DISPLAY(ITEM : in DUMMY);
type NO_OF_APPLES is new INTEGER;
type COUNTER is range 0 .. 1_000_000;
type ANSWER is (YES, NO, MAYBE);
procedure DISPLAY_APPLES is new DISPLAY(NO_OF_APPLES); -- 1
procedure DISPLAY_COUNTERS is new DISPLAY(COUNTER); -- 2
procedure DISPLAY_ANSWERS is new DISPLAY(ANSWER); -- 3
No, when the generic part says type DUMMY is range <>;, the procedure may be
instantiated for any integer type. NO_OF_APPLES is a type derived from
INTEGER, so it's an integer type.
1HPlease type a space to go on, or B or Q to go back to the question. 2272 463B459Q459$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ generic
type DUMMY is range <>;
procedure DISPLAY(ITEM : in DUMMY);
type NO_OF_APPLES is new INTEGER;
type COUNTER is range 0 .. 1_000_000;
type ANSWER is (YES, NO, MAYBE);
procedure DISPLAY_APPLES is new DISPLAY(NO_OF_APPLES); -- 1
procedure DISPLAY_COUNTERS is new DISPLAY(COUNTER); -- 2
procedure DISPLAY_ANSWERS is new DISPLAY(ANSWER); -- 3
No, when the generic part says type DUMMY is range <>;, the procedure may be
instantiated for any integer type. The definition of the user-defined type
COUNTER has the word range, so COUNTER is an integer type.
1HPlease type a space to go on, or B or Q to go back to the question. 3115 464B459$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ TASKING
Here are two versions of a program with two parallel tasks. Since the main
program is a task, these two versions are equivalent.
with TEXT_IO; use TEXT_IO; with TEXT_IO; use TEXT_IO;
procedure TASK_DEMO is procedure TASK_DEMO is
task A; task A;
task body A is task body A is
begin begin
PUT_LINE("a"); PUT_LINE("a");
PUT_LINE("a"); PUT_LINE("a");
end A; end A;
begin task B;
PUT_LINE("b"); task body B is
PUT_LINE("b"); begin
end TASK_DEMO; PUT_LINE("b");
PUT_LINE("b");
end B;
begin
null;
end TASK_DEMO;
1HPlease type a space to go on, or B to go back. 2727 465B463$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Our program could have specified as many tasks as we like. Also, our tasks
could have declarations between task body and begin. If the computer has
several processors, and the Ada compiler makes use of that fact, the tasks
could actually run simultaneously. Otherwise, the compiler may (but doesn't
have to) write code to time slice among the tasks, making them appear to run
simultaneously. One version of Ada we tried time slices, and the output of the
program looked something like this:
ba
ba
This happened because PUT_LINE is equivalent to PUT plus NEW_LINE, and thus
PUT_LINE can get interrupted before the CR-LF is output. Here one task printed
"b", the other task printed "a", and then both tasks sent CR-LF.
Another implementation of Ada we tried won't time-slice unless told to with a
pragma, covered in the Advanced Topics section. So the output of the same
program with that version of Ada looked like this:
1HPlease type a space to go on, or B to go back. 2446 466B464$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$a
a
b
b
In this case one task ran to completion before the other task started. The
point is that both versions of Ada ran our program correctly, but with
different results.
When data is passed between tasks, we often don't want the process interrupted.
For example, suppose one task updates a record with several fields, such as a
DATE. Another task reads the record. We don't want the second task to
interrupt the first in the middle of updating a record. Otherwise, the second
task might read an updated DAY field, an updated MONTH field, and an old YEAR
field, which would be meaningless. Ada has an elegant solution to this
problem, called the rendezvous.
In this example, we assume that the main program created both procedure CALLER
and task SERVER, and defined type DATE:
1HPlease type a space to go on, or B to go back. 3173 467B465$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ procedure CALLER is task SERVER is
D : DATE; entry UPDATE(ITEM : in out DATE);
end SERVER;
begin task body SERVER is
-----; begin
-----; -- Block 1 -----; -- Block 3
-----; -----;
accept UPDATE(ITEM : in out DATE) do
SERVER.UPDATE(D); -----; -- Block 4
-----;
-----; end UPDATE;
-----; -- Block 2 -----; -- Block 5
-----; -----;
end CALLER; end SERVER;
Code blocks 1 and 3 run in parallel (perhaps simultaneously, as discussed).
Then CALLER waits at the call to SERVER.UPDATE while SERVER executes block 4.
Block 4 is called the critical section of code, where records might be updated,
etc. When this rendezvous is over, blocks 2 and 5 run in parallel. If CALLER
reaches the call before SERVER reaches accept, CALLER will wait patiently
there for SERVER. If SERVER reaches accept first, it will wait patiently there
for a caller.
1HPlease type a space to go on, or B to go back. 3362 468B466$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ procedure CALLER is task SERVER is
D : DATE; entry UPDATE(ITEM : in out DATE);
end SERVER;
begin task body SERVER is
-----; begin
-----; -- Block 1 -----; -- Block 3
-----; -----;
accept UPDATE(ITEM : in out DATE) do
SERVER.UPDATE(D); -----; -- Block 4
-----;
-----; end UPDATE;
-----; -- Block 2 -----; -- Block 5
-----; -----;
end CALLER; end SERVER;
The call to UPDATE looks like a procedure call. We can't use a task, so the
call requires dot notation. The entry statement looks like a procedure
specification, with entry replacing procedure. The task specification may have
any number of entry statements; if it has none, we write simply TASK SERVER;.
The accept block looks like a procedure without declarations, but accept
replaces procedure, and do replaces is begin. An accept with no arguments and
no statements may be written simply as accept UPDATE;.
1HPlease type a space to go on, or B to go back. 2546F469T470B467$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ procedure MASTER is task SLAVE is
entry SYNC;
end SLAVE;
task body SLAVE is
begin begin
-----; -----;
-----; -- Block 1 -----; -- Block 3
-----; -----;
SLAVE.SYNC; accept SYNC;
-----; -----;
-----; -- Block 2 -----; -- Block 4
-----; -----;
end MASTER; end SLAVE;
True or False? Statements in blocks 1 and 4 could execute simultaneously.
1HPlease press T for true or F for false, or B to go back. 2826 471B468Q468$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ procedure MASTER is task SLAVE is
entry SYNC;
end SLAVE;
task body SLAVE is
begin begin
-----; -----;
-----; -- Block 1 -----; -- Block 3
-----; -----;
SLAVE.SYNC; accept SYNC;
-----; -----;
-----; -- Block 2 -----; -- Block 4
-----; -----;
end MASTER; end SLAVE;
You're right! MASTER will wait at SLAVE.SYNC for SLAVE to reach accept, or
SLAVE will wait at accept for MASTER to reach SLAVE.SYNC;. Therefore, blocks 1
and 4 can't execute simultaneously.
1HPlease type a space to go on, or B or Q to go back to the question. 2769 471B468Q468$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ procedure MASTER is task SLAVE is
entry SYNC;
end SLAVE;
task body SLAVE is
begin begin
-----; -----;
-----; -- Block 1 -----; -- Block 3
-----; -----;
SLAVE.SYNC; accept SYNC;
-----; -----;
-----; -- Block 2 -----; -- Block 4
-----; -----;
end MASTER; end SLAVE;
False. MASTER will wait at SLAVE.SYNC for SLAVE to reach accept, or SLAVE will
wait at accept for MASTER to reach SLAVE.SYNC;. Therefore, blocks 1 and 4
can't execute simultaneously.
1HPlease type a space to go on, or B or Q to go back to the question. 2525 472B468$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$If several tasks call an entry before the server reaches accept, the calls are
queued first-in, first-out.
We can write a select block to accept any of several different calls:
select
accept A;
or
accept B(I : in INTEGER) do
-----;
end B;
or
accept C;
end select;
When select is reached, the task waits for a call to A or B or C. If calls to
more than one entry are pending, one will be chosen arbitrarily.
A delay statement, used in ordinary code, will delay a specified number of
seconds (plus any system overhead). For example,
1HPlease type a space to go on, or B to go back. 2633 473B471$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ A;
delay 5.0;
B;
will call A, delay five seconds (plus system overhead), and then call B.
However, when used in a select block, the meaning is a bit different. It's
used to implement an impatient server. For example,
select
accept A;
or
accept B;
or
delay 5.0;
C;
end select;
will wait up to five seconds for a call to A or B. If no call is received, C
will be called.
Guards can be used to switch alternatives of a select block on and off. For
example,
1HPlease type a space to go on, or B to go back. 3142 474B472$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ J : INTEGER;
...
select
when J = 1 =>
accept A;
or
when J = 2 =>
accept B;
end select;
Here A is an alternative only if the condition (J = 1) is true; B is an
alternative only if J = 2. If J /= 1, then no call to A will be accepted, even
if one is pending. If every branch of a select block has a guard and all
guards are false, PROGRAM_ERROR is raised.
Tasks "die" in three ways. The least elegant way is for a task to abort it,
e.g., abort SERVER;. This is drastic, because SERVER might be in the middle
of a rendezvous. A better way is for a family of tasks each to include
terminate; as one alternative in a select block. (A "family" of tasks is the
set of tasks created by one "parent," for example, the main program.) When
calls to the entries in the tasks all cease, all tasks in the family will reach
the terminate alternative, and all will die together.
1HPlease type a space to go on, or B to go back. 3256 475B473$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$But the most orderly way for a task to die is for it simply to reach its last
statement. For example, task T below will continue to accept calls to T.A and
T.B until a task calls T.SHUTDOWN. At that time, T will die.
task T is task body T is
entry A; DONE : BOOLEAN := FALSE;
entry B; begin
entry SHUTDOWN; while not DONE loop
end T; select
accept A do
-----;
end A;
or
accept B do
-----;
end B;
or
accept SHUTDOWN;
DONE := TRUE;
end select;
end loop;
end T;
1HPlease type a space to go on, or B to go back. 2950 476B474$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Trying to call an entry of a task that has died will raise TASKING_ERROR.
Also, a task can't terminate until all the tasks it creates terminate. In
particular, the main program can't return to the operating system until all
tasks in the program have died. Programmers must be careful to avoid possible
deadlocks. Ada solves many problems that plague other languages, but
unfortunately the deadlock problem remains unsolved.
A select block may have an else alternative. Here's an example of a very
impatient server. If a call to A or B is pending it will be served, otherwise,
C will be called:
select
accept A do
-----;
end A;
or
accept B do
-----;
end B;
else
C;
end select;
1HPlease type a space to go on, or B to go back. 3263T477F478B475$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$type DATE is ... task body DATA_PROTECTOR is
task DATA_PROTECTOR is SAVE_D : DATE;
entry READ_DATE(D : out DATE); DONE : BOOLEAN := FALSE;
entry WRITE_DATE(D : in DATE); begin
entry SHUTDOWN; accept WRITE_DATE(D : in DATE) do
end DATA_PROTECTOR; SAVE_D := D:
end WRITE_DATE;
while not DONE loop
select
accept READ_DATE(D : out DATE) do
D := SAVE_D;
end READ_DATE;
or
accept WRITE_DATE(D : in DATE) do
SAVE_D := D;
True or False? This task must end WRITE_DATE;
serve at least one call to or
WRITE_DATE before it will accept SHUTDOWN;
accept calls to READ_DATE. DONE := TRUE;
end select;
end loop;
end DATA_PROTECTOR;
1HPlease press T for true or F for false, or B to go back. 3356 479B476Q476$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$type DATE is ... task body DATA_PROTECTOR is
task DATA_PROTECTOR is SAVE_D : DATE;
entry READ_DATE(D : out DATE); DONE : BOOLEAN := FALSE;
entry WRITE_DATE(D : in DATE); begin
entry SHUTDOWN; accept WRITE_DATE(D : in DATE) do
end DATA_PROTECTOR; SAVE_D := D:
end WRITE_DATE;
while not DONE loop
select
accept READ_DATE(D : out DATE) do
D := SAVE_D;
end READ_DATE;
or
accept WRITE_DATE(D : in DATE) do
SAVE_D := D;
You're right! The extra acceptend WRITE_DATE;
block outside the loop forces us or
to call WRITE_DATE at least once accept SHUTDOWN;
before we can call READ_DATE. DONE := TRUE;
end select;
end loop;
end DATA_PROTECTOR;
1HPlease type a space to go on, or B or Q to go back to the question. 3317 479B476Q476$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$type DATE is ... task body DATA_PROTECTOR is
task DATA_PROTECTOR is SAVE_D : DATE;
entry READ_DATE(D : out DATE); DONE : BOOLEAN := FALSE;
entry WRITE_DATE(D : in DATE); begin
entry SHUTDOWN; accept WRITE_DATE(D : in DATE) do
end DATA_PROTECTOR; SAVE_D := D:
end WRITE_DATE;
while not DONE loop
select
accept READ_DATE(D : out DATE) do
D := SAVE_D;
end READ_DATE;
or
accept WRITE_DATE(D : in DATE) do
SAVE_D := D;
True. The extra accept block end WRITE_DATE;
outside the loop forces us to or
call WRITE_DATE at least once accept SHUTDOWN;
before we can call READ_DATE. DONE := TRUE;
end select;
end loop;
end DATA_PROTECTOR;
1HPlease type a space to go on, or B or Q to go back to the question. 2629 480B476$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$The select block can be used in a caller as well as a server. The following
block waits up to five seconds to call entry A in task T. If T isn't ready to
accept the call in five seconds, the block calls procedure B instead. This is
called an impatient customer:
select
T.A;
or
delay 5.0;
B;
end select;
A very impatient customer can be implemented with else. This block calls T.A
only if T is ready to accept the call immediately, otherwise, it calls B.
select
T.A;
else
B;
end select;
1HPlease type a space to go on, or B to go back. 2441 481B479$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Task types may be declared. This permits us to create an array of tasks, and
it lets us bring tasks into existence via access types. Tasks begin executing
as soon as they're brought into existence. For example,
task type X is
entry E;
end X;
type P is access X;
X1 : P;
A : array(1 .. 10) of X;
task body X is
...
end X;
Entries to these tasks are called thus:
A(5).E;
X1 := new X;
X1.all.E; or just X1.E;
1HPlease type a space to go on, or B to go back. 2435 482B480$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Ada comes with a package CALENDAR; the specification is in section 9.6 of the
LRM. The part that concerns us here is shown below. Type DURATION is a fixed
point type built into Ada; the delay statement discussed earlier takes an
object of type DURATION.
package CALENDAR is
type TIME is private;
function CLOCK return TIME;
function "+"(LEFT : TIME; RIGHT : DURATION) return TIME;
function "-"(LEFT : TIME; RIGHT : TIME) return DURATION;
...
end CALENDAR;
Not shown are a few other operators, and subprograms to convert between type
TIME and the year, month, day, and number of seconds since midnight.
Let's write a program segment that uses CALENDAR and calls A every five
seconds:
1HPlease type a space to go on, or B to go back. 2627 483B481$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with CALENDAR; use CALENDAR;
...
NEXT_EVENT : TIME := CLOCK + 5.0;
...
loop
delay NEXT_EVENT - CLOCK;
A;
NEXT_EVENT := NEXT_EVENT + 5.0;
end loop;
Note that this loop accounts for the time required to call A. Instead of
delaying 5.0, we calculate the time of the next call in NEXT_EVENT, and delay
that time minus the current time, which we obtain by calling CLOCK. Thus the
program will go through the loop once every 5.0 seconds, even if it takes a
little time to call A.
The - and + operators inside the loop both use infix functions from CALENDAR.
We're now ready for Outside Assignment 6! It will be much simpler than Outside
Assignment 5.
1HPlease type a space to go on, or B to go back. 2840 484B482$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ OUTSIDE ASSIGNMENT 6 - EXERCISE IN TASKING
On page 32 of your printed course notes is a listing of TASKING.DUM. This
program calls a task entry to print Tick! on the screen every five seconds
until it has been printed nine times.
Every time delay is executed, there's a call to NEW_LINE, so that the delay
will be visible on the screen. The program is entirely in lower case, because
your assignment is to modify it. If you make your modifications in upper case,
it will be easy to see what you changed.
We want you to change the declaration of T from a single task to an array of
three tasks. The tasks are numbered 1, 2, and 3. Task 1 is to be activated
every two five-second intervals. Task 2 is to be activated every three five-
-second intervals, and task 3, every four. Also, instead of printing Tick!,
each task will identify itself by number, for example, Task number 3 is
starting. Output should look as shown on page 34 of your printed notes.
1HPlease type a space to go on, or B to go back. 2246 485B483$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$We recommend that you create an array of three counters. Each counter counts
down from its period (2, 3, or 4) to zero by one count every interval. When a
counter reaches zero, the corresponding task entry is called, and the counter
is reset to its period. All three counters should be initialized to zero, so
that all three tasks print their messages immediately upon activation of the
program. You should use the rendezvous mechanism to inform each task of its
number (1, 2, or 3). Your program should do an orderly shutdown of all three
tasks at the end.
Here are the steps to follow for Outside Assignment 6. They're also in your
printed course notes on page 33:
1HPlease type a space to go on, or B to go back. 2334 486B484$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$1. Make a copy of TASKING.DUM by typing COPY TASKING.DUM TASKING.ADA.
Compile, link, and execute the program to make sure it prints Tick! nine
times.
2. Edit TASKING.ADA to become your solution. Make your changes in upper case.
3. Compile TASKING.ADA, link, and execute.
4. Compare your output with page 34 of the printed course notes. If there are
any errors, go back to step 2.
5. When your output agrees with the printed course notes, you've finished the
assignment and will have a chance to compare your solution with ours.
Please type X to exit ADA-TUTR temporarily, and try Outside Assignment 6. Work
at your own pace; there's no deadline. Good luck!
1HPlease type X to exit, a space to go on, or B to go back. 1855 487B485$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ Congratulations on Completing Outside Assignment 6!
If you like, you can compare your solution with ours, which is in TASKING.ANS.
A listing is on page 35 of your printed course notes. Your solution might be
different from ours, but if your output agrees with page 34 of the printed
notes, your solution is correct.
You've learned a great deal of Ada! Let's go on to discuss some advanced
topics.
1HPlease type a space to go on, or B to go back. 2732 488B486$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ RENAMING
A subprogram can be renamed in Ada. This allows us to avoid the dot notation
without a use clause. For example, if our program withs TEXT_IO, we can write:
procedure PRINT(OBJECT : in STRING) renames TEXT_IO.PUT_LINE;
We can now call PRINT instead of TEXT_IO.PUT_LINE. The old name is still
available. Note that renaming can change the names of the formal parameters
("dummy arguments"). Renaming may also add, delete, or change default values.
When used in a package, a renaming declaration like the above goes in the
specification, not the body.
We can also rename task entries as procedures. This is the only way to avoid
the dot notation when calling a task entry.
A function can be renamed as an infix operator, if it has the right number and
types of arguments. Also, an infix operator can be renamed as a function. For
example, earlier we defined type VECTOR and wrote:
1HPlease type a space to go on, or B to go back. 2667 489B487$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ function "*"(LEFT, RIGHT : in VECTOR) return FLOAT;
This could be renamed as follows:
function DOT_PRODUCT(X, Y : in VECTOR) return FLOAT renames "*";
Renaming can get around the restriction that library subprograms can't be infix
operators. We can use a normal function name for the library, and rename it as
an infix operator for our program. Similarly, we can get around the rule that
library subprograms can't overload each other. We can give subprograms
different names in the library, and rename them in our program to overload each
other.
An attribute that takes an argument, such as PRED and SUCC, can be renamed as a
function. Record components can be renamed. If D is of the type DATE we had
earlier, we can write J : INTEGER renames D.YEAR;. Exceptions can also be
renamed, as in
OOPS : exception renames TEXT_IO.NAME_ERROR;
1HPlease type a space to go on, or B to go back. 3414 490B488$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ PACKAGES STANDARD AND ASCII
Ada comes with a package STANDARD. However, unlike all the other packages,
STANDARD is needed by every Ada compilation unit. Therefore, STANDARD is
automatically withed and used in every compilation. It need not be mentioned
in a context clause. STANDARD contains the definitions built into the Ada
language, such as type BOOLEAN is (FALSE, TRUE);. A listing of the package
specification is in Appendix C of the LRM. Thus, the full name for the type
BOOLEAN is STANDARD.BOOLEAN, the full name for INTEGER is STANDARD.INTEGER,
etc. Naturally, this normally need not concern the programmer. The dot
notation is automatic because STANDARD is automatically used in every
compilation.
However, inside package STANDARD is a package ASCII. Since this package is
part of STANDARD, we never have to write a with clause for it. But ASCII isn't
automatically used. If we want the dot notation for ASCII to be automatic, we
have to provide a use clause. As the listing in the LRM shows, ASCII contains
names for all the unprintable ASCII characters, such as BEL, ESC, etc. It also
provides names for many punctuation marks, in case your terminal or printer
doesn't have them. For example, DOLLAR, AT_SIGN, etc. Finally, it provides
names for all lower case letters, from LC_A to LC_Z.
1HPlease type a space to go on, or B to go back. 2159 491B489$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$For example, either of the following programs will print a@b and ring the bell
Note the placement of use ASCII; in the second example. It's similar to the
placement of use MY_INT_IO; in ADD.ADA, which we discussed early in the course.
1HPlease type a space to go on, or B to go back. 2863 492B490$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ AN ALTERNATIVE TO INFIX NOTATION
Earlier we learned to define and use infix operators like
type VECTOR is array(INTEGER range <>) of FLOAT;
function "*"(LEFT, RIGHT : in VECTOR) return FLOAT;
A, B : VECTOR(1 .. 10);
F : FLOAT;
...
F := A * B;
An alternative notation equivalent to F := A * B; is F := "*"(A, B);. Why
would anyone want to use this clumsier notation? If our function is in a
package MATH that the calling program withs but for some reason doesn't use, we
could use dot notation and write F := MATH."*"(A, B);. But we couldn't use dot
notation directly with infix operators, as in F := A MATH.* B; or even
F := A MATH."*" B;. Both of those are illegal. The alternative notation is
also used to emphasize that an operator comes from package STANDARD. For
example, if I, J, and K are INTEGERs, we could write I := STANDARD."*"(J, K);,
which is equivalent to I := J * K;.
1HPlease type a space to go on, or B to go back. 1558249314943495B491$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Assuming X, Y, and Z have been declared FLOAT, which one of the following is
illegal?
1. X := Y / Z;
2. X := Y STANDARD."/" Z;
3. X := STANDARD."/"(Y, Z);
1HPlease press 1, 2, or 3, or B to go back. 1757 496B492Q492$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. X := Y / Z;
2. X := Y STANDARD."/" Z;
3. X := STANDARD."/"(Y, Z);
You're right! The syntax of number 2 is illegal. To specify the package
STANDARD, we have to use the syntax of number 3. Normally, of course, the
syntax of number 1 is used.
1HPlease type a space to go on, or B or Q to go back to the question. 1614 496B492Q492$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. X := Y / Z;
2. X := Y STANDARD."/" Z;
3. X := STANDARD."/"(Y, Z);
No, number 1 is the syntax that would ordinarily be used for division, and is
legal.
1HPlease type a space to go on, or B or Q to go back to the question. 1564 496B492Q492$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. X := Y / Z;
2. X := Y STANDARD."/" Z;
3. X := STANDARD."/"(Y, Z);
No, number 3 is the correct way to specify package STANDARD explicitly.
1HPlease type a space to go on, or B or Q to go back to the question. 2637 497B492$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ RECORD DISCRIMINANTS AND RECORD VARIANTS
The definition of a record type can have discriminants, which have the same
form as formal parameters ("dummy arguments") of subprograms, except that the
mode is omitted. Default values may be supplied. For example,
type MATRIX is array(INTEGER range <>, INTEGER range <>) of FLOAT;
type SQUARE_MATRIX(SIZE : POSITIVE := 9) is
record
SQ : MATRIX(1 .. SIZE, 1 .. SIZE);
end record;
Although objects of type MATRIX can be rectangular, objects of type
SQUARE_MATRIX must be square. In declaring these objects, we use the same
syntax as a subprogram call, with either positional or named notation:
A : SQUARE_MATRIX(7); -- a 7-by-7 matrix
B : SQUARE_MATRIX(SIZE => 5); -- a 5-by-5 matrix
C : SQUARE_MATRIX; -- a 9-by-9 matrix, using the default value of SIZE
1HPlease type a space to go on, or B to go back. 2938 498B496$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Of course, subtypes of discriminated records can be declared. For example,
subtype CHESS_BOARD is SQUARE_MATRIX(SIZE => 8);
A record discriminant is used in the definition of type TEXT in the
TEXT_HANDLER package specification of section 7.6 of the LRM:
MAXIMUM : constant := ... ;
subtype INDEX is INTEGER range 0 .. MAXIMUM;
...
type TEXT(MAXIMUM_LENGTH : INDEX) is
record
POS : INDEX := 0;
VALUE : STRING(1 .. MAXIMUM_LENGTH);
end record;
With the simplified version of type TEXT presented earlier, every object of
type TEXT occupied enough memory for the longest string we expected to handle
(e.g., 80 characters). With this version, each object of type TEXT that we
create can have just the MAXIMUM_LENGTH we need, and its effective length POS
can vary from zero to that MAXIMUM_LENGTH. The record discriminant complicates
the package specification only very slightly; see section 7.6 of the LRM.
1HPlease type a space to go on, or B to go back. 2972 499B497$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$The definition of a record type can have a variant, which also has the same
form as a subprogram formal parameter without the mode. However, the syntax of
a case construct is used to specify part of the record. Although a record can
have several discriminants, it can have only one variant, and the variant part
must appear last in the record. For example,
type SEX_TYPE is (MALE, FEMALE);
type PERSON(SEX : SEX_TYPE) is
record
AGE : NATURAL;
case SEX is
when MALE =>
BEARDED => BOOLEAN;
when FEMALE =>
CHILDREN : NATURAL;
end case;
end record;
If the sex of the person is MALE, we want the record to include a BOOLEAN
showing whether he's bearded, but if the sex is FEMALE, we want the record to
include an INTEGER (subtype NATURAL), showing the number of children she has.
1HPlease type a space to go on, or B to go back. 2761 500B498$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type SEX_TYPE is (MALE, FEMALE);
type PERSON(SEX : SEX_TYPE) is
record
AGE : NATURAL;
case SEX is
when MALE =>
BEARDED => BOOLEAN;
when FEMALE =>
CHILDREN : NATURAL;
end case;
end record;
Objects are declared and given values as we'd expect:
JOHN : PERSON(SEX => MALE) := (SEX => MALE, AGE => 21, BEARDED => FALSE);
MARY : PERSON(SEX => FEMALE) := (SEX => FEMALE, AGE => 18, CHILDREN => 0);
Attempting to access JOHN.CHILDREN or MARY.BEARDED will raise CONSTRAINT_ERROR.
To simplify the object declarations, subtypes may be declared:
subtype MAN is PERSON(MALE);
subtype WOMAN is PERSON(FEMALE);
1HPlease type a space to go on, or B to go back. 214225011502B499$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$type COMPUTER_SIZE is (HANDHELD,LAPTOP,PORTABLE,DESKTOP,MAINFRAME,CLUSTER);
Which commented declaration in the above program is illegal?
1HPlease press 1 or 2, or B to go back. 2249 503B500Q500$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$type COMPUTER_SIZE is (HANDHELD,LAPTOP,PORTABLE,DESKTOP,MAINFRAME,CLUSTER);
You're right! The initialization of COMPANY_LAN fails to include fields for
NUMBER_OF_UNITS and DATA_RATE.
1HPlease type a space to go on, or B or Q to go back to the question. 2348 503B500Q500$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$type COMPUTER_SIZE is (HANDHELD,LAPTOP,PORTABLE,DESKTOP,MAINFRAME,CLUSTER);
No, the declaration of MY_PC and its initialization are correct. The others
clause of the case applies, so there are only two fields in the record when
SIZE is DESKTOP.
1HPlease type a space to go on, or B or Q to go back to the question. 2827 504B500$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ FIXED POINT AND UNIVERSAL TYPES
The only fixed point type defined in package STANDARD is DURATION. However,
Ada lets us define our own fixed point types. We specify the accuracy with the
reserved word delta, and a range constraint is required. For example,
type VOLTAGE is delta 0.01 range -20.0 .. 20.0;
This guarantees that the objects of type VOLTAGE will be represented with at
least an accuracy of 1/100. Since the computer is binary, Ada will choose an
internal representation at least as accurate as 1/128. It might use even
greater accuracy, for example, 1/256. In any event, it's guaranteed that the
accuracy is at least as good as that requested.
It's possible to make a request that a particular implementation of Ada can't
handle. For example, if we write
type VOLTAGE is delta 1.0E-10 range 0.0 .. 1.0E9;
the version of Ada we're using may have to report that it has no internal
representation that satisfies this requirement.
1HPlease type a space to go on, or B to go back. 2771 505B503$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type VOLTAGE is delta 0.01 range -20.0 .. 20.0;
The set of numbers that can be represented exactly by any Ada that accepts a
type definition like the above is called the model numbers of that type. This
applies to floating types as well as fixed, for example,
type W is digits 5 range 0.0 .. 100.0;
A particular implementation may represent additional numbers exactly; these are
called safe numbers. The safe numbers are a superset of the model numbers;
their range usually is a little larger.
We can add and subtract objects of a fixed point type. However, if we multiply
or divide them, we must immediately convert the result to the same or another
numeric type before we can store it. For example,
V1, V2, V3 : VOLTAGE;
...
V1 := V2 + V3; -- legal
V1 := V2 * V3; -- illegal
V1 := VOLTAGE(V2 * V3); -- legal
1HPlease type a space to go on, or B to go back. 2773 506B504$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$TEXT_IO contains a generic package FIXED_IO for I/O of fixed point types.
When we declare a variable in Ada, we give its type. But when we declare a
constant, we may or may not give its type. For example,
L : constant INTEGER := 30;
M : constant := 1000;
E : constant FLOAT := 2.718281828;
PI : constant := 3.141592654;
Also, when we write a number, such as 3.0 or 29_999, we usually don't qualify
it with a type (for example, FLOAT'(3.0)).
Suppose an implementation of Ada provides types INTEGER, LONG_INTEGER, FLOAT,
and LONG_FLOAT. How can Ada determine the types of M, PI, 3.0, and 29_999? M
and 29_999 are said to be of type universal_integer; they can assume any
integer type as required. PI and 3.0 are said to be of type universal_real and
can assume any floating or fixed point type as required.
We can't explicitly declare objects to be of universal types. However, we can
write
1HPlease type a space to go on, or B to go back. 2825 507B505$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ M : constant := 1000;
PI : constant := 3.141592654;
I : INTEGER;
J : LONG_INTEGER;
F : FLOAT;
G : LONG_FLOAT;
...
I := M; J := M;
I := 29_999; J := 29_999;
F := PI; G := PI;
F := 3.0; G := 3.0;
and in each case the constant assumes the correct type. The result of
multiplying or dividing two numbers of a fixed point type is said to be of type
universal_fixed. This result must be explicitly converted to some numeric
type before it can be stored.
Most of the attributes that produce integer results, like POS, are of type
universal_integer. For example, with the declarations above, we could write
I := CHARACTER'POS('A');
J := CHARACTER'POS('A');
1HPlease type a space to go on, or B to go back. 16363508150925104511B506$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Which one of the following declarations is illegal?
1. type RATE is digits 6;
2. type DISTANCE is digits 6 range 0.0 .. 1.0E6;
3. type CURRENT is delta 0.1;
4. type TEMP is delta 0.05 range -200.0 .. 450.0;
1HPlease press 1, 2, 3, or 4, or B to go back. 1726 512B507Q507$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. type RATE is digits 6;
2. type DISTANCE is digits 6 range 0.0 .. 1.0E6;
3. type CURRENT is delta 0.1;
4. type TEMP is delta 0.05 range -200.0 .. 450.0;
You're right! A fixed point type declaration must have a range constraint.
1HPlease type a space to go on, or B or Q to go back to the question. 1730 512B507Q507$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. type RATE is digits 6;
2. type DISTANCE is digits 6 range 0.0 .. 1.0E6;
3. type CURRENT is delta 0.1;
4. type TEMP is delta 0.05 range -200.0 .. 450.0;
No, number 1 is legal. A user defined floating point type need not have a
range constraint.
1HPlease type a space to go on, or B or Q to go back to the question. 1725 512B507Q507$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. type RATE is digits 6;
2. type DISTANCE is digits 6 range 0.0 .. 1.0E6;
3. type CURRENT is delta 0.1;
4. type TEMP is delta 0.05 range -200.0 .. 450.0;
No, number 2 is legal. A user defined floating point type may have a range
constraint.
1HPlease type a space to go on, or B or Q to go back to the question. 1722 512B507Q507$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. type RATE is digits 6;
2. type DISTANCE is digits 6 range 0.0 .. 1.0E6;
3. type CURRENT is delta 0.1;
4. type TEMP is delta 0.05 range -200.0 .. 450.0;
No, number 4 is legal. A fixed point type declaration must have a range
constraint.
1HPlease type a space to go on, or B or Q to go back to the question. 3351 513B507$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ MORE ATTRIBUTES
Ada provides a wide variety of attributes. All of them are listed and defined
in Appendix A of the LRM. The most important ones that we haven't yet
discussed are these:
FIRST and LAST can be used with any scalar type or subtype (including floating
and fixed), not just discrete types and subtypes. For example, FLOAT'LAST is
the highest number your particular Ada represents with type FLOAT.
For any real type or subtype (floating or fixed), SMALL and LARGE are the
smallest and largest positive model numbers. Thus FLOAT'SMALL is the
difference between zero and the next larger number in type FLOAT. Also, for
any floating point (sub)type, EPSILON is the difference between one and the
next larger number. We'll use EPSILON in a generic function later.
For a floating point (sub)type, DIGITS returns the value given for digits in
the declaration, and for a fixed point (sub)type, DELTA returns the value given
for delta in the declaration. These attributes may not seem too useful,
because the programmer already knows what he wrote in the declarations.
However, they're used in generic packages and subprograms. For example, if the
generic part says type DUMMY is delta <>;, the body can use DUMMY'DELTA.
1HPlease type a space to go on, or B to go back. 3545 514B512$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$For any discrete (sub)type, WIDTH gives the maximum length that the attribute
IMAGE can produce. For example, with our earlier definition of RAINBOW_COLOR,
RAINBOW_COLOR'WIDTH is 6. For versions of Ada using 16-bit INTEGERs,
INTEGER'WIDTH is also 6. BOOLEAN'WIDTH is 5.
COUNT is used with the name of a task entry. It returns the number of calls
presently queued on the entry. TERMINATED is of type BOOLEAN. It's used with
a task name, and tells if the task is terminated.
Let's write a generic function to compute the square root for any floating
point type, using Newton-Raphson iteration. This method simply says that if G
is a guess of the square root of X, the next guess is the average of G and X/G.
For example, if we want to compute the square root of 9.0 and our first guess
is 9.0, successive guesses are 5.0, 3.4, 3.02352941, 3.00009155, 3.00000000.
Note that convergence is very rapid. However, the problem in writing a program
is knowing when to stop the iteration. We'll use the attribute EPSILON. Since
G*G/X should be 1.0, we'll quit when the difference between G*G/X and 1.0 is
less than or equal to 3.0 times EPSILON. Recall that DUMMY'EPSILON is the
difference between 1.0 and the next higher number for type DUMMY. If we use
1.0 times EPSILON, the loop might never terminate, and if we use 10.0 times
EPSILON, we might not get full precision. So we'll use 3.0 times EPSILON.
Here's our function:
1HPlease type a space to go on, or B to go back. 2351 515B513$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ generic
type DUMMY is digits <>;
function SQRT(X :in DUMMY) return DUMMY;
function SQRT(X :in DUMMY) return DUMMY is
GUESS : DUMMY := X;
begin
if X < 0.0 then
raise NUMERIC_ERROR;
end if;
while X /= 0.0 and then abs(GUESS*GUESS/X - 1.0) > 3.0*DUMMY'EPSILON loop
GUESS := (X/GUESS + GUESS) * 0.5;
end loop;
return GUESS;
end SQRT;
We tested our SQRT with a version of Ada having types FLOAT, LONG_FLOAT, and
LONG_LONG_FLOAT. The last gives at least 33 decimal digits of precision. SQRT
was instantiated for all three floating point types, as was FLOAT_IO to display
the results. When tested with the three types, all displayed digits of the
answers were correct.
1HPlease type a space to go on, or B to go back. 3235 516B514$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ SEQUENTIAL_IO AND DIRECT_IO
TEXT_IO creates, reads and writes text files that can be typed on the screen or
printed. Ada also provides packages SEQUENTIAL_IO and DIRECT_IO, which create,
read, and write binary files. These files usually can't be typed or printed,
but they tend to be more efficient than text files, because the computer
doesn't have to convert numbers between its internal representation and ASCII
to read and write binary files.
SEQUENTIAL_IO and DIRECT_IO are both generic, and can be instantiated for any
type. The specifications are in sections 14.2.3 and 14.2.5 of the LRM. Like
TEXT_IO, they have procedures to CREATE, OPEN, and CLOSE files, but the I/O
procedures are called READ and WRITE, rather than PUT, GET, PUT_LINE, and
GET_LINE. SEQUENTIAL_IO always reads and writes sequentially, but DIRECT_IO is
capable of random access. In DIRECT_IO, an optional extra argument in READ and
WRITE tells the procedure the position in the file to read FROM or write TO.
TEXT_IO and instantiations of SEQUENTIAL_IO and DIRECT_IO each define their own
FILE_TYPE, so we can't open a file with one package and then do I/O with
another. DIRECT_IO provides a FILE_MODE of INOUT_FILE as well as the usual
IN_FILE and OUT_FILE.
1HPlease type a space to go on, or B to go back. 2714 517B515$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$If you like, you can examine the file ADA-TUTR.ADA for an example of the use of
DIRECT_IO. ADA-TUTR creates a subtype for a block of characters and then
instantiates DIRECT_IO for that subtype. It then opens ADA-TUTR.DAT with mode
IN_FILE so that it can read blocks of characters by random access. This
enables ADA-TUTR to find and display any screen quickly. The preliminary
comments in ADA-TUTR.ADA describe the format of the data file ADA-TUTR.DAT in
detail.
You may also want to examine the files DAT2TXT.ADA and TXT2DAT.ADA. These two
programs are used when installing ADA-TUTR on non-PC computers. Their use is
described on page 5 of your printed course notes. They with both TEXT_IO and
DIRECT_IO, because they access a text file as well as a random access file.
However, to avoid confusion between the two packages, they use neither TEXT_IO
nor the instantiation of DIRECT_IO. Dot notation is used instead.
1HPlease type a space to go on, or B to go back. 1839151825193520B516$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Which commented line is illegal?
with TEXT_IO, SEQUENTIAL_IO; use TEXT_IO, SEQUENTIAL_IO; -- 1
procedure IO is
subtype LINE is STRING(1 .. 80);
type SCREEN is array(1 .. 24) of LINE;
package LINE_IO is new SEQUENTIAL_IO(LINE); use LINE_IO; -- 2
package SCREEN_IO is new SEQUENTIAL_IO(SCREEN); use SCREEN_IO; -- 3
begin
null;
end IO;
1HPlease press 1, 2, or 3, or B to go back. 2169 521B517Q517$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO, SEQUENTIAL_IO; use TEXT_IO, SEQUENTIAL_IO; -- 1
procedure IO is
subtype LINE is STRING(1 .. 80);
type SCREEN is array(1 .. 24) of LINE;
package LINE_IO is new SEQUENTIAL_IO(LINE); use LINE_IO; -- 2
package SCREEN_IO is new SEQUENTIAL_IO(SCREEN); use SCREEN_IO; -- 3
begin
null;
end IO;
You're right! We can't use a generic package, only its instantiations,
because we can't call the subprograms in a generic package. The first line
should read
with TEXT_IO, SEQUENTIAL_IO; use TEXT_IO; -- 1
1HPlease type a space to go on, or B or Q to go back to the question. 1912 521B517Q517$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO, SEQUENTIAL_IO; use TEXT_IO, SEQUENTIAL_IO; -- 1
procedure IO is
subtype LINE is STRING(1 .. 80);
type SCREEN is array(1 .. 24) of LINE;
package LINE_IO is new SEQUENTIAL_IO(LINE); use LINE_IO; -- 2
package SCREEN_IO is new SEQUENTIAL_IO(SCREEN); use SCREEN_IO; -- 3
begin
null;
end IO;
No, the instantiation of SEQUENTIAL_IO for the subtype LINE is correct.
1HPlease type a space to go on, or B or Q to go back to the question. 1911 521B517Q517$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ with TEXT_IO, SEQUENTIAL_IO; use TEXT_IO, SEQUENTIAL_IO; -- 1
procedure IO is
subtype LINE is STRING(1 .. 80);
type SCREEN is array(1 .. 24) of LINE;
package LINE_IO is new SEQUENTIAL_IO(LINE); use LINE_IO; -- 2
package SCREEN_IO is new SEQUENTIAL_IO(SCREEN); use SCREEN_IO; -- 3
begin
null;
end IO;
No, the instantiation of SEQUENTIAL_IO for the type SCREEN is correct.
1HPlease type a space to go on, or B or Q to go back to the question. 2936 522B517$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ SUBPROGRAM PARAMETERS WITH GENERICS
The generic part of a subprogram or package can specify a dummy subprogram as
well as a dummy type. This is similar to using subprograms as arguments
(parameters) in Algol and Pascal, and to using the little-known keyword
EXTERNAL in Fortran. In Ada, we simply precede the dummy subprogram
specification with the keyword with in the generic part. This use of the word
with has nothing to do with context clauses. For example, here's the
specification of a generic function that has one dummy function specification
in the generic part:
generic
with function DUMMY(X : in FLOAT) return FLOAT;
function DEFINITE_INTEGRAL(LOWER_LIMIT, UPPER_LIMIT : in FLOAT) return FLOAT;
We could then write a function COS, instantiate DEFINITE_INTEGRAL for it, and
use the instantiation as follows:
ANSWER : FLOAT;
function COS(X : in FLOAT) return FLOAT:
function DEFINITE_INTEGRAL_OF_COS is new DEFINITE_INTEGRAL(COS);
1HPlease type a space to go on, or B to go back. 2755 523B521$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ REPRESENTATION CLAUSES AND SYSTEM
Ada normally represents an enumeration type internally with successive integers
starting at zero. For example, if we write
type COMMAND is (LEFT, RIGHT, FORWARD, BACK);
the compiler will normally represent LEFT with 0, RIGHT with 1, etc. Usually
this doesn't concern the programmer. However, after the above declaration, we
can specify the internal representation with a representation clause like this:
for COMMAND use (LEFT => 1, RIGHT => 2, FORWARD => 4, BACK => 8);
We might want to do that if, for example, we're sending a value of type COMMAND
to some hardware which will interpret the bit patterns. The values must be
assigned in increasing order with no duplications, but gaps are permitted. The
attributes SUCC, PRED, POS, and VAL are not affected. Thus COMMAND'POS(BACK)
is still 3.
We can specify the SIZE, in bits, of the objects of a given type:
1HPlease type a space to go on, or B to go back. 2772 524B522$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type NUM is range 0 .. 100;
for NUM'SIZE use 8;
We can specify the STORAGE_SIZE (in bits!) for a task and for a collection of
accessed objects like a linked list. If MONITOR is a task and the
specification for our linked list says type P is access LINK;, we can write
for MONITOR'STORAGE_SIZE use 16_384*8;
for P'STORAGE_SIZE use 32_768*8;
The attributes SIZE and STORAGE_SIZE can also be used in the usual way:
I : INTEGER := MONITOR'STORAGE_SIZE;
We can specify the attribute SMALL for a fixed point type:
type VOLTAGE is delta 0.01 range -20.0 .. 20.0;
for VOLTAGE'SMALL use 1.0/128.0;
Before discussing the remaining types of representation clauses, we must
briefly mention the package SYSTEM that comes with Ada. SYSTEM contains
implementation dependent specifications.
1HPlease type a space to go on, or B to go back. 3161 525B523$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$A brief outline of package SYSTEM is in section 13.7 of the LRM. However, the
full package specification should appear in the documentation that came with
your compiler. For all compilers that meet the Ada Standard, the description
of implementation dependent features (including the specification of package
SYSTEM) is always in Appendix F of the documentation. Of interest here are the
type ADDRESS and the constant STORAGE_UNIT. In our examples, we'll assume that
SYSTEM.ADDRESS is some integer type.
Representation clauses can use the reserved word at followed by a constant of
type SYSTEM.ADDRESS to specify the absolute address of a variable, a constant,
a task entry, a procedure, or a package. The package SYSTEM must be visible.
This feature is useful for memory-mapped I/O and interrupt handlers, etc. For
example:
MODEM_CONTROL : INTEGER;
for MODEM_CONTROL use at 16#FC00#;
task INTERRUPT_HANDLER is
entry CLOCK_INTERRUPT;
for CLOCK_INTERRUPT use at 16#100#;
end INTERRUPT_HANDLER;
procedure KEYSTROKE;
for KEYSTROKE use at 16#200#;
1HPlease type a space to go on, or B to go back. 2860 526B524$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Finally, we can use at, mod, and range with constants of type
SYSTEM.STORAGE_UNIT to specify how records are stored. For example,
type VERY_SHORT_INTEGER is range 0 .. 15;
type PACKED is
record
A, B, C, D : VERY_SHORT_INTEGER;
end record;
for PACKED use
record at mod 2;
A at 0 range 0 .. 3;
B at 0 range 4 .. 7;
C at 1 range 0 .. 3;
D at 1 range 4 .. 7;
end record;
This forces A and B to be stored in bits 0 .. 3 and 4 .. 7 of byte 0 of the
record, and C and D to be packed into byte 1. The optional clause record at
mod 2; specifies that all records of type PACKED will begin at even addresses.
An implementation of Ada need not accept most representation clauses to meet
the standard. If any clause is rejected, an error message will be printed.
1HPlease type a space to go on, or B to go back. 146625271528B525$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type ANSWER is (YES, NO, MAYBE);
for ANSWER use (YES => 1, NO => 2, MAYBE => 4);
What is ANSWER'VAL(2)?
1. ANSWER'VAL(2) is NO.
2. ANSWER'VAL(2) is MAYBE.
1HPlease press 1 or 2, or B to go back. 1665 529B526Q526$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type ANSWER is (YES, NO, MAYBE);
for ANSWER use (YES => 1, NO => 2, MAYBE => 4);
You're right! The representation clause doesn't affect the attributes POS and
VAL, and positions are numbered from zero. So ANSWER'VAL(2) is MAYBE.
1HPlease type a space to go on, or B or Q to go back to the question. 1626 529B526Q526$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ type ANSWER is (YES, NO, MAYBE);
for ANSWER use (YES => 1, NO => 2, MAYBE => 4);
No, the representation clause doesn't affect the attributes POS and VAL, and
positions are numbered from zero. So ANSWER'VAL(2) is MAYBE.
1HPlease type a space to go on, or B or Q to go back to the question. 2833 530B526$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ UNCHECKED CONVERSION AND UNCHECKED DEALLOCATION
Ada comes with a generic function UNCHECKED_CONVERSION and a generic procedure
UNCHECKED_DEALLOCATION. They can be instantiated for any type. Both are
somewhat dangerous to use, but we'll describe them briefly. Their
specifications are:
generic
type SOURCE is limited private;
type TARGET is limited private;
function UNCHECKED_CONVERSION(S : SOURCE) return TARGET;
generic
type OBJECT is limited private;
type NAME is access OBJECT;
procedure UNCHECKED_DEALLOCATION(X : in out NAME);
UNCHECKED_CONVERSION "converts" from one type to another without doing any
arithmetic or bit manipulation. In other words, it lets us look at an object
of one type as if it were of another type. The effect is similar to the use
of EQUIVALENCE in Fortran. The results may be unpredictable unless the two
types occupy the same amount of storage.
1HPlease type a space to go on, or B to go back. 3215 531B529$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$One use of UNCHECKED_CONVERSION might be to allow us to and two INTEGERs. Some
Ada compilers come with a package that enables us to do that, but many
compilers have no such package. Suppose that types INTEGER and BOOLEAN occupy
the same amount of storage. If our program says with UNCHECKED_CONVERSION; we
could write
function INT_TO_BOOL is new UNCHECKED_CONVERSION(INTEGER, BOOLEAN);
function BOOL_TO_INT is new UNCHECKED_CONVERSION(BOOLEAN, INTEGER);
function "and"(LEFT, RIGHT : in INTEGER) return INTEGER is
begin
return BOOL_TO_INT(INT_TO_BOOL(LEFT) and INT_TO_BOOL(RIGHT));
end "and";
Using UNCHECKED_CONVERSION usually destroys program portability.
UNCHECKED_DEALLOCATION allows us to free the memory occupied by an object
associated with a pointer. Normally, the system reclaims memory when it's
needed. However, the execution time for that so-called garbage collection
tends to be long and unpredictable. Suppose we have type P is access LINK; and
HEAD : P;. Also suppose that we no longer need the object pointed to by HEAD,
and we're sure that no other pointer points to the same object as HEAD. If our
program says with UNCHECKED_DEALLOCATION; we can write
1HPlease type a space to go on, or B to go back. 2050 532B530$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ procedure FREE is new UNCHECKED_DEALLOCATION(LINK, P);
...
FREE(HEAD);
This will release the memory occupied by the object pointed to by HEAD, and
then set HEAD to null. But there's a danger. If there's another pointer that
pointed to the same object, it now points to released memory. A reference to
that pointer will have unpredictable results. In general, it's best to let the
system handle the reclaiming of memory. That way there's no danger of dangling
references.
1HPlease type a space to go on, or B to go back. 3130 533B531$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ PRAGMAS
A pragma is a message to the compiler. The pragmas that are predefined by Ada
are all described in Appendix B of the LRM; we'll discuss the most important
ones here. A particular version of Ada need not implement all the predefined
pragmas, and it may add some of its own. (One version of Ada adds a pragma
TIME_SLICE, used with tasking.) Unlike representation clauses, unimplemented
predefined pragmas do not cause error messages; the compiler simply ignores
them. This enhances program portability. Any additional pragmas added by a
particular implementation of Ada will be explained in Appendix F of the
compiler documentation. The most important predefined pragmas are these:
The statements pragma LIST(ON); and pragma LIST(OFF); turn on and off the
compiler listing. Also, pragma PAGE; will cause the compiler listing to start
a new page, if the listing is turned on. These pragmas are allowed almost
anywhere in the program.
Within the declarative region we can write pragma OPTIMIZE(TIME); or pragma
OPTIMIZE(SPACE); to ask the compiler to optimize the program for minimum
execution time or minimum memory usage.
1HPlease type a space to go on, or B to go back. 3361 534B532$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$We can write pragma INLINE(...); with the name of a subprogram to ask the
compiler to write inline code in place of every call to the subprogram. Even
versions of Ada that implement this pragma will ignore it if the subprogram is
recursive.
We can interface a subprogram written in another language by writing pragma
INTERFACE(..., ...); after the subprogram specification. The two arguments are
the name of the language and the subprogram name. Consult the compiler
documentation for information on bringing the object file into the Ada library.
We can ask the compiler to minimize memory occupied by a record or array by
writing, after the type declaration, pragma PACK(...); with the name of the
type. Note that the specification for package STANDARD (in Appendix C of the
LRM) contains pragma PACK(STRING); after the definition of type STRING.
Package SYSTEM defines a subtype of INTEGER called PRIORITY. We can assign a
priority to a task by writing, in the specification, pragma PRIORITY(...); with
an argument of subtype SYSTEM.PRIORITY. Higher numbers denote greater urgency.
The pragma SUPPRESS can be used to ask the compiler to turn off certain checks,
such as CONSTRAINT_ERROR. It's dangerous and shouldn't be used unless
absolutely necessary because of time or memory constraints.
1HPlease type a space to go on, or B to go back. 1514253515363537B533$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$In the author's opinion, which one of these is not dangerous?
1. UNCHECKED_DEALLOCATION
2. pragma PACK
3. pragma SUPPRESS
1HPlease press 1, 2, or 3, or B to go back. 1949 538B534Q534$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. UNCHECKED_DEALLOCATION
2. pragma PACK
3. pragma SUPPRESS
You're right! The worst pragma PACK could do is slow the program down, and
this pragma is used in package STANDARD. UNCHECKED_DEALLOCATION could allow a
pointer to point to memory that has been released, with unpredictable results.
SUPPRESS could allow a program to use a subscript that's out of range, etc.
1HPlease type a space to go on, or B or Q to go back to the question. 1652 538B534Q534$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. UNCHECKED_DEALLOCATION
2. pragma PACK
3. pragma SUPPRESS
No, UNCHECKED_DEALLOCATION is dangerous because it could allow a pointer to
point to memory that has been released, with unpredictable results.
1HPlease type a space to go on, or B or Q to go back to the question. 1612 538B534Q534$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 1. UNCHECKED_DEALLOCATION
2. pragma PACK
3. pragma SUPPRESS
No, SUPPRESS is dangerous because it could allow a program to use a subscript
that's out of range, etc.
1HPlease type a space to go on, or B or Q to go back to the question. 3328 539B534$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ LOOSE ENDS AND PITFALLS
In this final section, we cover some miscellaneous topics that were omitted
earlier for simplicity. We also mention some common errors made by Ada
programmers. Beginners aren't expected to understand every paragraph until
they've gained more experience, and we won't ask questions in this section.
Some terminals and printers don't support the entire ASCII character set. In
an Ada program, the vertical bar | may be replaced with the exclamation mark !,
as in when 3 ! 5 =>. Also, a pair of sharp signs # may be replaced with a pair
of colons :, as in 16:FC03:. The quotation marks around a string constant may
be replaced with percent signs if the string doesn't contain any quotation
marks. In that case, any percent signs within the string must be doubled.
These character replacements shouldn't be used in programs if the equipment
will support the standard characters.
An expression is called static if it can be evaluated at compile time. In
almost every case where a constant normally appears, a static expression may
also be used. For example, an address representation clause normally takes
a constant of type SYSTEM.ADDRESS. A static expression of this type is also
acceptable, as in for CLOCK_INTERRUPT use at 16*16;.
1HPlease type a space to go on, or B to go back. 3622 540B538$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$The unary minus is always an operator and never part of a constant. Thus -5
is actually a static expression and not a constant. Normally, this doesn't
concern the programmer, because, as we just said, static expressions can
usually appear where a constant appears. However, in a few special situations
we can get into trouble. For example, we can write for I in 10 .. 20 loop and
A : array(10 .. 20) of FLOAT; but we can't omit the words INTEGER range in
for I in INTEGER range -10 .. 10 loop and A : array(INTEGER range -10 .. 10) of
FLOAT;! Also, if a package P declares type COUNT is new INTEGER; then the
unary minus operator for that type is part of the package. If our program
withs but doesn't use P, we can write A : P.COUNT := 1; but not
B : P.COUNT := -1;. We either have to use the package, rename P."-", or write
B : P.COUNT := P."-"(1);.
The operators have precedence, so that 1 + 2 * 3 means 1 + (2 * 3). The
precedence of all the operators is given in section 4.5 of the LRM. A
programmer should never have to look these up, because parentheses should be
used for any cases that aren't obvious. Unary minus has a low precedence, so
-A mod B means -(A mod B).
If we write A, B : array(1 .. 5) of FLOAT; then A and B have different
anonymous types, and we can't write A := B;. To fix this, write
type VECTOR5 is array(1 .. 5) of FLOAT; and then A, B : VECTOR5;.
1HPlease type a space to go on, or B to go back. 2840 541B539$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Ada will automatically convert from a universal type to a named type, but not
from a named type to a universal. For example,
C1 : constant INTEGER := 1; -- legal
C2 : constant INTEGER := 2; -- legal
C3 : constant := C1 + C2; -- illegal
When arrays are assigned, the subscripts don't have to match; only the lengths
and types of the elements need match. But if a formal parameter ("dummy
argument") of a subprogram is a constrained array, the subscripts in the call
to the subprogram must match. For example, the last line here will raise
CONSTRAINT_ERROR:
subtype NAME is STRING(1 .. 30);
JOHN : NAME;
LINE : STRING(1 .. 80);
procedure DISPLAY(PERSON : in NAME);
...
JOHN := LINE(51 .. 80); -- legal
DISPLAY(LINE( 1 .. 30)); -- legal
DISPLAY(LINE(51 .. 80)); -- illegal
1HPlease type a space to go on, or B to go back. 2847 542B540$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$When a subprogram formal parameter is an unconstrained array, beginners often
wrongly assume that the subscripts will start with one. For example,
LINE : STRING(1 .. 80);
procedure DISPLAY(S : in STRING) is
begin
for I in 1 .. S'LENGTH loop
... S(I) ...
This will raise CONSTRAINT_ERROR if we call DISPLAY(LINE(51 .. 80));. The for
statement should be changed to say for I in S'RANGE loop.
Remember that elaboration occurs at run time. The following raises
PROGRAM_ERROR by trying to activate a task before elaborating its body:
task type T is ... end T;
type P is access T;
T1 : P := new T;
task body T is ... end T;
The third line should be changed to T1 : P; and the statement T1 := new T;
should be placed in the executable region.
1HPlease type a space to go on, or B to go back. 3134 543B541$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$A return statement in a function is used with an object: return ANSWER;.
However, return may also appear without an object in a procedure; we simply
write return;. Normally, a procedure returns after executing its last
statement, but an early return is possible by this method. Well structured
programs don't use this feature of Ada.
Some implementations of Ada provide a package LOW_LEVEL_IO which includes
overloaded procedures SEND_CONTROL and RECEIVE_CONTROL to interface various
hardware devices directly. This package is completely implementation
dependent, so you'll have to consult Appendix F of your compiler documentation.
In the rare case of an aggregate containing just one element, we must use named
rather than positional notation. For example, the last line is illegal in
type VECTOR is array(INTEGER range <>) of FLOAT;
A : VECTOR(1 .. 1);
...
A := (1 => 2.3); -- legal
A := (2.3); -- illegal
because the right hand side is a FLOAT rather than an array of one FLOAT. It's
OK to use positional notation in calls to subprograms with only one argument.
1HPlease type a space to go on, or B to go back. 2739 544B542$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$In the unusual case of a for loop index hiding an explicitly declared object of
the same name, the explicitly declared object can be accessed inside the loop.
Simply use dot notation with the name of the compilation unit (procedure,
function, etc.) For example, the following is legal:
procedure MAIN is
IX : FLOAT;
J : INTEGER;
begin
IX := 3.2;
for IX in 1 .. 10 loop
MAIN.IX := 6.0;
J := IX;
end loop;
end MAIN;
Inside the loop, IX refers to the loop index, and the explicitly declared
object can be accessed by writing MAIN.IX. Outside the loop, IX refers to the
explicitly declared object, and the loop index doesn't exist.
1HPlease type a space to go on, or B to go back. 3028 101B543$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Well, we haven't covered all there is to know about Ada, but this has been a
very thorough course. If you've come this far and completed the six Outside
Assignments, you should be a very good Ada programmer. To be an excellent Ada
programmer, start doing all your casual programming in Ada. If you need a
simple program to balance your checkbook, write it in Ada! At this point,
switching to Ada for all your programming will do you much more good than
further instruction from a tutor program.
The best way to answer any remaining questions about Ada is to "ask the
compiler" by writing a brief test program, especially if your compiler is
validated. You can also look in the LRM, which, by definition, does cover all
of the Ada language. However, the LRM isn't easy reading!
The best way to debug a short program is often to execute it by hand, with
pencil and paper. You can also add extra statements to the program to display
intermediate results, and remove them later.
We wish you success with Ada, and welcome your comments and suggestions!